yandex-direct-manager · git:20261029.3e5ebf5 · 2026-10-29 · sha256 1ae4887fec64f56e

yandex-direct-manager git:20261029.3e5ebf5A

Immutable. This exact content never changes and is served at /api/v1/blob/1ae4887fec64f56e.

---
name: "yandex-direct-manager"
description: Тактический пайплайн создания рекламной кампании в Яндекс.Директе через MCP — сбор семантики через Wordstat, кластеризация на НЧ/СЧ/ВЧ и Поиск/РСЯ, минусация (3 уровня + кросс), создание объявлений по лимитам Директа, аудит счётчика и настройка целей в Метрике через MCP, выбор стратегии торгов и заливка в DRAFT. Используй когда пользователь говорит «сделай РК в Директе», «собери семантику», «нужны ключи и минусы», «напиши объявления», «запусти кампанию», «залей через MCP». Умеет выполнять отдельный этап — только семантика, только минусы, только объявления — без полного брифа. Не для аналитики, не для оптимизации работающих кампаний, не для стратегии бренда — только создание новой кампании от брифа до DRAFT.
---

# Yandex Direct — пайплайн создания кампании

Скилл проводит маркетолога от брифа до залитой в **DRAFT** рекламной кампании в Яндекс.Директе. Фокус — только **создание**: семантика → кластеризация → минусы → объявления → заливка. Аналитика, оптимизация уже работающих кампаний, исследования рынка — **не задача этого скилла**.

Пайплайн опирается на три MCP: `yandex-direct` (прогноз CPC, аудит аккаунта, дедупликация, заливка кампании целиком), `yandex-wordstat` (частотности и сбор семантики на Шагах 1 и 3) и **`yandex-metrika`** (счётчик, история трафика и конверсий сайта, аудит и создание целей и сегментов — Шаги 0.5, 7, 10). Все — **хостовые серверы aihub.click.ru**: локально ничего не запускается, Bun и клон репозитория не нужны. Подключение — `scripts/setup_yandex_direct_mcp.py` (пишет конфиги Cursor / Claude Code / Claude Desktop), общий справочник — `docs/hosted-mcp-setup.md` репозитория пакета. Скилл не проверяет подключение MCP заранее — если на каком-то шаге инструмент не отвечает, обрабатываем ошибку по месту: Шаг 2 уходит на справочник CPC, Шаги 1 и 3 — на `scripts/wordstat_api.py` или браузер, Шаг 7 — на Management API с токеном или инструкции для UI, Шаг 10 — на Директ Коммандер.

## Что нужно от среды

**Обязательный минимум — две способности:**

- **Чтение и запись файлов** в рабочей папке `direct-campaigns/<slug>/`. На этом держится весь пайплайн: каждый шаг порождает артефакт, следующий шаг его читает.
- **Диалог с пользователем** — задать вопрос, получить ответ, дождаться подтверждения. Без этого не работают гейты `[GATE: маркетолог]`, а они здесь не формальность: заливка идёт только после явного «ОК».

Больше ничего обязательного нет. Всё перечисленное ниже — **необязательно**: без любой из этих способностей скилл доходит до конца, но часть данных недобрана.

**Необязательные способности.** У каждой есть ветка «если нет». Идёшь по ветке — **печатаешь пометку о пропуске** в артефакт этого шага (формат ниже).

| Способность | Где нужна | Если нет |
|---|---|---|
| **Запуск кода** — Python 3.9+ и команды оболочки | Шаги 1, 2, 3, 8, 10 (`scripts/*`) | Отдай команду пользователю текстом, попроси прислать вывод и работай с ним как со своим. Если и это невозможно: Шаги 1 и 3 — браузерный пробив частотностей или список от маркетолога; Шаг 8 и Шаг 10 — ручная сверка лимитов по `references/yandex-direct-specs.md` вместо `preflight.py`; Шаг 10 — Директ Коммандер вместо `generate_ads_xlsx`. **Теряется:** автоматическая проверка лимитов Директа перед заливкой — самая дешёвая защита от отклонённых объявлений. |
| **Дочерние агенты** — субагент или форк контекста | Шаги 3–4 | Выполни Шаги 3–4 инлайн по `subagents/semantics.md` и `references/wordstat-filter.md`. Не пересказывай CSV в чат — работай через файлы. **Теряется:** изоляция контекста; тысячи ключей Wordstat останутся в основном разговоре и вытеснят решения Шагов 0–2 к моменту написания объявлений. |
| **Чтение веб-страниц по URL** | Шаг 0 (сайт продукта); Шаг 0.5.2 — только если MCP Метрики не нашёл счётчик (проверка тега на сайте) | Шаг 0 — попроси у маркетолога 5 пунктов словами (H1, бенефиты, CTA, цена/триал, доказательная база), помечай `source: со слов клиента`. Шаг 0.5.2 — попроси прислать `counter_id` из интерфейса Метрики. **Теряется:** сверка заявленного с тем, что реально на сайте. |
| **Поиск в интернете** | Шаг 8 (сверка отстройки с топ-3 выдачи) | Пиши тексты по УТП из брифа, без сверки с конкурентами. **Теряется:** проверка, что заголовки действительно отличают продукт, а не повторяют выдачу. |
| **Вызов MCP-инструментов** (хостовые серверы `yandex-direct`, `yandex-wordstat`, `yandex-metrika`, aihub.click.ru) | Директ: **0.5 (кабинет, счётчики кампаний, черновики)**, шаги 2, 5, 8, 10. Wordstat: шаги 1, 3. Метрика: **0.5 (счётчик, связка с кабинетом, данные сайта)**, 7, 10 | Шаг 2 — справочник CPC из `references/frequency-calculator.md` вместо живого аукциона; Шаг 5 — регионы по справочнику; Шаг 8 — дедупликация ключей по выгрузке от маркетолога; Шаг 10 — Директ Коммандер по `references/direct-commander-import.md`; Шаги 1 и 3 — `scripts/wordstat_api.py` (нужен ключ Cloud) или браузерный пробив по `references/wordstat-browser.md`. Без MCP Метрики: Шаг 0.5.2 — `counter_id` со слов маркетолога, данные сайта не снимаются; Шаг 7 — Management API с OAuth-токеном или инструкции для UI. **Теряется:** живые данные аккаунта и сайта, автоматическая заливка и автоматический пробив частотностей — кампанию, семантику и цели собирает человек руками. |
| **Загрузка картинок в хранилище** — MCP `KeepImage` (`storage_publish_image`) или HTTP через `scripts/upload_creatives_to_storage.py` | Шаг 10Б — заливка РСЯ-картинок | **Не нужна, если `adimages_add` принимает путь к файлу** рабочей папки (так на платформе AI Hub — Шаг 10Б). Иначе публичной ссылки для `adimages_add(image_url)` не будет: объявление заливается **без** `image_hashes`, картинки уходят в `10_launch_log.md` списком ручной работы (Директ/Коммандер). Картинки, которые маркетолог дал сразу публичной ссылкой, заливаются и без хранилища. **Теряется:** автоматическая заливка своих/сгенерированных креативов. |
| **Генерация картинок инструментом `create_ad_image`** (есть на платформе AI Hub) | Шаг 8.5 — картинки РСЯ | Скрипт `generate_creative_images.py` с ключом OpenAI (нужны запуск кода и сетевые вызовы), иначе ТЗ дизайнеру. **Теряется:** ничего, если доступен скрипт; без него — готовые картинки, кампания уходит без визуалов. |
| **Браузер под сессией пользователя** | Шаг 1, Шаг 3 — только когда нет ключа Wordstat | Попроси маркетолога пробить список руками (`references/wordstat-browser.md` → «Пробивает маркетолог»). Не может — непробитые фразы помечай `frequency: n/a`. **Прикидку не подставляй:** выдуманная частотность хуже отсутствующей, на ней стоит весь расчёт бюджета. |
| **Сетевые вызовы внешних API по ключу** — Метрика и Wordstat, оба только как фолбек без MCP | Шаг 7 (фолбек); шаги 1, 3 (фолбек) | Шаг 7 без MCP Метрики и без API — режим «инструкции для UI»: скилл готовит план целей, маркетолог заводит их руками. Шаги 1 и 3 без MCP `yandex-wordstat` и без ключа Cloud — браузерный пробив или список ключей от маркетолога. **Теряется:** автоматическая настройка целей, а в фолбеке — автоматический сбор семантики. |

**Отсутствие инструмента в списке — ещё не отсутствие инструмента.** Прежде чем уйти на фолбек «MCP нет», проверь два случая: (1) сервер называется не так, как в тексте (ищи по имени инструмента — `*accounts_get`, `*wordstat_shows_batch`, `*storage_publish_image`, `*yandex_metrika_counters_get`, — а не по имени сервера: Метрика в сессии может называться `metrikaCM`); (2) среда подгружает инструменты отложенно и на старте сессии их ещё не видно — в такой среде сначала поищи инструмент её механизмом поиска (в Claude Code — `ToolSearch`), дождись подключения серверов и только потом решай. Ложный фолбек дорог: браузерный пробив вместо одного вызова на 1000 фраз, Коммандер вместо автозаливки.

**Названия инструментов в тексте ниже — примеры.** Где встречаются `WebFetch`, `Task`, `claude-in-chrome`, `mcp__yandex-direct__*`, `mcp__yandex-wordstat__*`, `mcp__yandex-metrika__*` — это имена из одной конкретной среды, приведённые для наглядности. Ориентируйся на **способность**, а не на имя инструмента: если в твоей среде он называется иначе, но делает то же — используй его.

### Пометка о пропуске

Шаг выполнен без необязательной способности — впиши в артефакт этого шага строку такого вида:

> ⚠️ **пропущено: нет `<способность или источник данных>`** — `<что сделал вместо>`. Потеряно: `<что именно недобрано>`. Как добрать: `<что сделать человеку>`.

Например:

> ⚠️ **пропущено: нет доступа к Вордстату** — частотности не пробиты, в `01_masks.json` стоит `frequency: n/a`. Потеряно: отсев масок ниже порога 150 показов/мес. Как добрать: пробить список на wordstat.yandex.ru и вернуться на Шаг 1.

Все пометки шагов **продублируй сводным списком** в `10_launch_log.md`, раздел «Пропущено из-за среды». Маркетолог должен видеть все пробелы в одном месте, а не искать по десяти файлам. Молча упрощённый результат не отдаём.

### Блокер

Пометка о пропуске значит «сделал хуже, но сделал». **Блокер** значит «дальше нельзя, пока
человек не решит». Блокер не мешает готовить черновик (семантику, структуру, объявления можно
собирать), но **запрещает любую запись в кабинет** (Шаг 10Б) и показывается в каждой сводке
гейта, пока не снят. Список — в `_state.json` → `blockers`, формат:

> ⛔ **блокер: `<что именно>`** — `<почему это нельзя обойти>`. Что сделать: `<конкретное действие человека, со ссылкой, если есть>`. Снимается: `<как скилл поймёт, что снято>`.

| Ситуация | Блокер, а не пропуск, потому что |
|---|---|
| Пустое стоп-поле брифа (Шаг 0, только `mode: full`) и маркетолог не ответил | Скилл не подставляет критичные настройки сам |
| Нет доступа к кабинету Директа: MCP не подключён, кабинет не виден, ошибка авторизации | Залить некуда. Выход — подключить MCP (`docs/hosted-mcp-setup.md`) или выбрать выгрузку в Директ Коммандер: тогда кампанию заливает человек |
| Интеграция Метрики не подключена в click.ru | Без неё цели не видны. Ссылка: https://help.click.ru/97#connect-yandex-metrica |
| Счётчика на сайте нет или визитов ноль | Конверсии не будут считаться |
| Цели не видны, не созданы или не подтверждены маркетологом; доступ к счётчику только `view` и создать цель нельзя | Оптимизировать не на что |

**Цель Метрики никогда не подменяется догадкой.** Нет подтверждённого маркетологом `goal_id` —
блокер «нет цели для оптимизации», а не «возьму похожую по названию».

## Главный принцип

**Каждый шаг порождает артефакт.** Рабочая папка: `direct-campaigns/<slug>/`. Артефакты накапливаются и являются входами для следующих шагов. На ключевых шагах — гейт `[GATE: маркетолог]` с явным «ОК» перед движением дальше. Заливка происходит **только в DRAFT**. Активация — всегда руками маркетолога.

## Чего скилл не делает: прямой отказ

Эти действия скилл не выполняет ни по просьбе, ни после «ОК». На платформе AI Hub они
к тому же закрыты в шлюзе:

| Просят | Где сделать руками | Что можешь предложить вместо |
|---|---|---|
| Удалить кампанию, группу, объявление, ключ | В интерфейсе Директа, в списке кампаний или объявлений | Черновик не показывается и денег не тратит — удалять его ради этого не нужно. До заливки — поправить или убрать объявление из плана |
| Запустить кампанию, снять с паузы | В интерфейсе Директа, после чек-листа 10А | Пройти чек-лист 10А вместе, чтобы запуск был безопасным |
| Отправить объявления на модерацию | В интерфейсе Директа, вместе с запуском | Вопросом: проверить тексты по требованиям модерации (`references/ad-copywriting.md`). Статус модерации называй только из поля `Status` объявления: черновик (`DRAFT`) на модерацию не отправлялся (`references/yandex-direct-mcp.md` → «Объявления») |
| Изменить что-то в кабинете, который пользователь не выбрал | Добавить кабинет в сессию (на платформе — окно выбора рекламных аккаунтов) | Вопросом: продолжить в выбранном кабинете или добавить этот кабинет в сессию и залить туда после «ОК» |

Исключение одно — служебная пробная кампания `_CPC_PROBE_<дата>`, которую скрипт прогноза
сам создаёт и удаляет на Шаге 2.

**Попросили такое — ответь сразу, до любого вызова инструмента (для чужого кабинета — одна
проверка, ниже), в одном сообщении:**

1. **Прямо, первой фразой:** «Нет, у меня нет прав на удаление объявлений». Назови действие
   словами пользователя. Не переспрашивай и не делай вид, что просьбы не было.
2. **Где руками** — из таблицы.
3. **Что можешь вместо** — если есть замена, предложи её вопросом и жди ответа.
4. **Стоп.** После отказа ничего не продолжай: не возвращайся к брифу, не переходи к
   следующему шагу, не генерируй картинки, не вызывай инструменты кабинета. Жди, что скажет
   человек.

**Чужой кабинет — одна проверка, потом отказ.** Чтобы понять, есть ли кабинет в сессии, можно
один раз прочитать список кабинетов (`accounts_get`) — молча, без промежуточного «проверяю».
Ответ человеку всё равно начинается с отказа: «Нет, кабинета <имя> нет в этой сессии — залить
туда не могу». Дальше — где его добавить и вопрос из таблицы. Не обещай залить сам, когда
кабинет появится: после добавления — заново разведка кабинета (Шаг 0.5.1) и гейт заливки.

Так же отвечай на любую просьбу, для которой у скилла нет инструмента или права: прямо
«этого я не могу», где это делается, что можешь вместо — и стоп.

**Вызов отклонён или инструмента нет.** Если нужного инструмента нет среди доступных или
ответ говорит, что действие запрещено, не разрешено, требует прав или кабинет не выбран
(`forbidden`, `not allowed`, `permission`, `403`), — это **отказ, а не сбой шлюза**. Не
повторяй вызов, не ищи смежный инструмент, не уходи на скрипт или фолбек шага и не
продолжай план. Ответь человеку по схеме выше: что именно нельзя и что можно вместо. Правила
повтора из «Сбоев шлюза» (`references/yandex-direct-mcp.md`) — только для `gateway 502`,
обрывов и таймаутов без слов о правах.

## Как использовать references

Длинные методологии лежат в `references/` — Claude открывает их в нужный момент.

| Reference | Когда читать | Зачем |
|---|---|---|
| `references/brief.md` | Шаг 0 | Шаблон нового брифа под короткий пайплайн |
| `references/mask-builder.md` | Шаг 1 | Методология двух колонок А (сужающие) × Б (общие) |
| `references/frequency-calculator.md` | Шаг 2 | Формула расчёта частотности + справочник CPC |
| `references/wordstat-filter.md` | Шаг 4 | Пороги НЧ<350/СЧ 350-750/ВЧ>750 + распределение Поиск/РСЯ |
| `references/campaign-structure.md` | Шаг 5 | Правила разбиения семантики на кампании и группы |
| `references/negative-keywords-builder.md` | Шаг 6 | 3 уровня минусов + кросс-минусация |
| `references/metrika-goals-setup.md` | Шаг 0.5.2 (раздел «Чем работаем» + Фаза 1, п. 1–2), Шаг 7 целиком | MCP Метрики: поиск счётчика, аудит целей по факту срабатываний, создание целей и сегментов; шаблоны целей по нишам; фолбек на Management API |
| `references/channel-history.md` | Шаг 0.5.3 целиком; Шаги 2 и 5 — «Как менеджер использует историю» | История каналов: контракт файла (общий с `yandex-direct-audit`), как попросить файл у маркетолога, черновики, пороги выводов |
| `references/yandex-direct-specs.md` | Шаг 8 | Лимиты Директа, форматы, операторы |
| `references/ad-copywriting.md` | Шаг 8 | Формулы заголовков и текстов, требования модерации |
| `references/rsya-creatives.md` | Шаг 8.5 | Техтребования Директа к РСЯ-креативам, workflow генерации по гейтам, чек-лист модерации |
| `references/visual-generation.md` | Шаг 8.5 | Генерация картинок через OpenAI (`generate_creative_images.py`): архитектура, brand.json, форматы |
| `references/bidding-strategy.md` | Шаг 9 | 3-фазная эволюция стратегии торгов |
| `references/yandex-direct-mcp.md` | Шаг 10 + при сбоях MCP | **Справочник MCP:** сигнатуры инструментов, единицы, `dry_run`, хостовое подключение, чего MCP не умеет |
| `references/mcp-account-integration.md` | Шаги 0.5.1, 2, 5, 10 | **Сценарии по шагам:** аудит аккаунта, дедупликация, оценка CPC, регионы и полная последовательность заливки (Use case 5) |
| `references/wordstat-mcp.md` | Шаги 1, 3 + при сбоях Wordstat | **Пробив частотностей через MCP `yandex-wordstat`** (`wordstat_shows_batch`) и цепочка путей расширения семантики (скрипт Cloud API / комбинаторика / браузер) |
| `references/wordstat-browser.md` | Шаг 1 / Шаг 3 (нет ни MCP, ни API-ключа) | Пробив частотностей через браузер пользователя (фолбек вместо симуляции) |
| `references/direct-commander-import.md` | Шаг 10 (фолбек) | Импорт xlsx в Директ Коммандер — когда MCP недоступен |
| `references/ad-legal-topics.md` | Шаг 0 + Шаг 8 | Факт-таблица лицензируемых/запрещённых тем: стоп-гейт и автопредупреждения |

## Как использовать scripts

- `scripts/preflight.py` — проверка `08_creatives.json` против лимитов Директа перед заливкой (Шаг 10). Ошибки — заливка не стартует
- `scripts/generate_ads_xlsx.py` — `08_creatives.json` → xlsx для маркетолога и для фолбека в Коммандер (или CSV)
- `scripts/generate_media_plan.py` — собирает артефакты пайплайна в `media_plan.docx`
- `scripts/keyword_helper.py` — операторы соответствия, базовые минус-листы
- `scripts/forecast_cpc.py` — оценка CPC: разбор `keywordbids_get`, прямой аукцион, либо прогноз по фразам (v4 / Click.ru DRAFT) без MCP `forecast_bids`
- `scripts/generate_creative_images.py` — генерация картинок Шага 8.5 (OpenAI gpt-image-1)
- `scripts/validate_assets.py` — проверка картинок/видео под техтребования Директа
- `scripts/upload_creatives_to_storage.py` — заливка локальных картинок в KeepImage по HTTP и получение публичных ссылок для `adimages_add(image_url=...)` (Шаг 10Б); пишет `assets/storage_manifest.json`. Токен — `clickru` из реестра ключей (его туда кладёт установщик `setup_yandex_direct_mcp.py` при подключении MCP; иначе — `manage_credentials set clickru`). `--check` / `--dry-run` / `--all-assets` / `--files`
- `scripts/manage_credentials.py` / `scripts/credentials.py` — реестр ключей (openai, yandex_direct, clickru) вне рабочей папки
- `scripts/wordstat_api.py` — **основной путь расширения семантики** (Шаг 3, путь 1): клиент Yandex Cloud Search API (Wordstat `topRequests`), `--mode masks` / `--mode semantics` (рекурсивный сбор волнами с ассоциациями); env `WORDSTAT_API_KEY` + `WORDSTAT_FOLDER_ID`; `--check` для проверки. MCP `yandex-wordstat` покрывает только частотности (`wordstat_shows_batch`), семантику не расширяет — см. `references/wordstat-mcp.md`
- `scripts/setup_yandex_direct_mcp.py` — подключение **хостовых** MCP `yandex-direct`, `yandex-wordstat` и `KeepImage` (хранилище картинок) (aihub.click.ru) к Cursor / Claude Code / Claude Desktop одной командой (по умолчанию `--server all`): токен click.ru, `--target`, `--dry-run`, `--remove`, `--click-ru-user-id`. Запускается, когда MCP в среде не видны. KeepImage подключается тем же токеном заголовком `X-Auth-Token` (не токеном в URL). Заодно сохраняет токен click.ru в реестр ключей (`clickru`), поэтому `upload_creatives_to_storage.py` работает сразу после подключения — отдельный `manage_credentials set clickru` не нужен. Локальный stdio-вариант (клон репозитория + Bun) остаётся для разработки самих серверов — см. `references/yandex-direct-mcp.md`

---

## Архитектура: оркестратор + субагенты

Скилл работает как **оркестратор** в основном контексте: держит бриф, `_state.json`, гейты и финальную сборку (медиаплан, заливка). **Самый тяжёлый шаг — сбор и кластеризация семантики — вынесен в изолированный субагент** `semantics` (форк), чтобы тысячи ключей из Wordstat не засоряли основной контекст к моменту генерации объявлений и заливки. Инструкция субагента — в `subagents/semantics.md` (это обычный файл-инструкция, а **не** второй `SKILL.md`).

| Шаг | Где исполняется | Почему |
|---|---|---|
| 0–2 (бриф, разведка аккаунта, маски, прогноз частотности) | **Инлайн** | Решения и гейты; отчёты Метрики короткие (`limit` ≤ 50) — в контекст не бьют |
| 3 — сбор семантики | **Субагент** `semantics` | Тысячи ключей из Wordstat |
| 4 — кластеризация | **Субагент** `semantics` | Чтение того же большого CSV |
| 5–10 (структура, минусы, цели, объявления, стратегия, заливка) | **Инлайн** | Анализ, синтез, заливка |

**Принцип возврата:** субагент собирает семантику в своём контексте, пишет артефакты на диск и возвращает наверх **только сводку ≤20 строк**. Гейт и решение — у маркетолога в основном контексте.

**Фолбек:** если субагент недоступен (старая среда, ошибка форка, путь не резолвится) — оркестратор выполняет Шаги 3-4 **инлайн** по тем же reference/scripts. Делегирование — оптимизация, а не жёсткая зависимость.

Субагент лежит в `subagents/semantics.md` (файл-инструкция, не `SKILL.md`); сам по инициативе модели не запускается — его порождает оркестратор. References и scripts — единые, в корне скилла; субагент читает их по пути от корня.

---

## Проверка версии (один раз за разговор, до Шага 0)

Версия этого скилла — в файле `VERSION` рядом с этим `SKILL.md`. Прочитай его и одним запросом
загрузи актуальную версию: `https://raw.githubusercontent.com/ai-hub-open/yandex-direct-manager/main/VERSION`.

- **Совпадает** — ничего не пиши, переходи к Шагу 0.
- **Загрузить не вышло** (нет сети, нет чтения по URL, ошибка) — не повторяй запрос, ничего не пиши
  и не ставь пометку о пропуске: на маршрут это не влияет. Переходи к Шагу 0.
- **На GitHub новее** (сравнивай как semver: 0.18.2 > 0.18.0) — одним коротким сообщением скажи,
  что вышла версия X (у пользователя Y), дай ссылку на
  [CHANGELOG](https://github.com/ai-hub-open/yandex-direct-manager/blob/main/CHANGELOG.md) и как обновиться:
  - Claude Code, скилл установлен плагином: `/plugin marketplace update ai-hub-open`, затем перезапустить сессию;
  - скилл загружен архивом (Claude Desktop / claude.ai) или папкой: скачать `yandex-direct-manager.skill`
    из [релизов](https://github.com/ai-hub-open/yandex-direct-manager/releases/latest) и заменить старый.

  Спроси: обновиться сейчас или продолжить на текущей версии. Ответ «продолжить» — работаем дальше,
  к вопросу в этом разговоре не возвращаемся.

**Сам файлы скилла не скачивай и не заменяй**, даже если среда это позволяет: обновление ставит
пользователь. Скилл, который сам переписывает свои инструкции из интернета, — дыра в безопасности.

---

# Режим одного шага

Не каждый запрос — полная кампания. Пользователь может хотеть только один этап, и тогда полный
бриф (бюджет, каналы, цель Метрики, кабинет) — лишние вопросы.

**Как распознать:** запрос называет один этап — «собери семантику / ключи», «пробей
частотности», «минусы», «напиши объявления» — без «сделай РК / собери кампанию». Или посреди
брифа пользователь говорит «только X», «Y не нужен». Сомневаешься — бери режим шага: из него
легко перейти в полный, а заданные лишние вопросы назад не забрать.

**«Запусти» — не «сделай».** «Запусти / включи / активируй» про то, что уже собрано (ключи,
объявления, залитый черновик), — это запуск: прямой отказ по разделу «Чего скилл не делает», а
не переход в полный режим. В режиме шага на «запусти рекламу по этим ключам» — первой фразой
«Нет, запускать рекламу я не могу: кампанию включает маркетолог в интерфейсе Директа», затем
один вопрос «Собрать по этим ключам полную кампанию черновиком?» — и стоп. В начале разговора,
когда собирать ещё нечего, «запусти рекламу для <продукт>» — просьба о полной кампании: одной
строкой скажи, что соберёшь её черновиком, а включает маркетолог.

| `mode` | Шаги | Входы — один блок, ≤4 вопроса | Пропускаем |
|---|---|---|---|
| `step:semantics` | 1, 3, 4 | продукт или сайт, регион, тип запросов (дефолт — ключи под Поиск и РСЯ, не спрашиваем), сужающие слова / углы | 0.5, 2. Бюджета нет → ВЧ на Поиск не берём: `04_keys_excluded.csv` со статусом `vch_no_budget`, пометка в `04_wordstat_filter_summary.md` |
| `step:negatives` | 6 | семантика (из `direct-campaigns/<имя>/` или файл от маркетолога), продукт, сужающие слова | всё остальное. Семантики нет → предложи `step:semantics`, полный маршрут не запускай. Нет `05_campaign_structure.json` → кросс-минусы по сужающим (колонка `narrowing`) или группам файла; делить не на что — `06_negative_keywords_cross.csv` пустой с пометкой |
| `step:ads` | 8 | УТП, посадочная, ключи / структура, если есть | 0.5, 2–7, 9, 10. MCP-проверки Шага 8 (дедупликация, CPC) без кабинета не делаем; `preflight.py` — делаем |

Вопросы — по `references/brief.md` → «Короткий бриф для режима одного шага». Ответы пиши в
`00_brief.md` в те же разделы; не спрошенные — «не спрашивали (режим шага)».

**Правила режима:**
- стоп-поля Шага 0 действуют только в `mode: full`; в режиме шага — короткий бриф;
- **стоп-гейт тематики** (`references/ad-legal-topics.md`) — всегда, в любом режиме. Тематику
  определи сам по продукту и сайту; о документах спроси, только если тема лицензируемая;
- гейты внутри выполняемого шага сохраняются; гейт брифа Шага 0 — нет (сводка брифа 2–3 строки);
- после последнего артефакта этапа (`step:semantics` — `04_*`, `step:negatives` — `06_*`,
  `step:ads` — `08_*`) — сводка 5–10 строк + одна строка «Дальше можно: <следующий этап> —
  скажи» и **стоп**. Гейт последнего шага и эта сводка — одно сообщение. Сам следующий этап не
  начинаешь. `_state.json` → `current_step: "<шаг>.done"`;
- `10_launch_log.md` в режиме шага нет: пометки о пропуске — в последний артефакт этапа и
  строкой «Пробелы» в финальную сводку;
- два этапа сразу («семантика и минусы») — по очереди, короткий бриф один на оба;
- пользователь прямо просит всю кампанию («сделай всю РК», «собери полную кампанию») —
  `mode: full`, полный бриф, но спрашиваешь только то, чего ещё нет в `00_brief.md`; готовые
  артефакты переиспользуешь. Любая другая формулировка, в том числе «запусти», — сначала вопрос
  «Собрать полную кампанию черновиком?» и стоп. В сообщении с этим вопросом нет вопросов полного
  брифа, кабинет и счётчик не проверяешь; `mode` меняешь только после «да»;
- `_state.json` → `mode: "full" | "step:semantics" | "step:negatives" | "step:ads"`. Нет поля
  (папка от версии до 0.20.0) — считай `full`.

---

# Workflow: 10 шагов

## Шаг 0. Бриф

**Определи режим** (раздел «Режим одного шага»). Полный бриф из 11 пунктов — только для `full`.

**Прочти `references/brief.md` целиком.** В нём — точная формулировка вопросов и формат `00_brief.md`.

**Пришёл бриф от `marketing-strategist`** (`12_brief_for_yandex_direct.md`) — он уже в формате этого брифа: перенеси поля, спроси только «❗ уточнить» и пустые стоп-поля, выбор каналов и УТП стратега не пересматривай. Приложенный с ним `channel_history.md` — сразу вход Шага 0.5.3.

Задай маркетологу **одним блоком** 11 пунктов брифа (продукт + посадочная + целевая аудитория, тип сайта, регион, бюджет, каналы, тип запросов, УТП + сужающие, цель в Метрике, CPA-таргет + ценность конверсии + коллтрекинг, существующие материалы, **лицензируемая/запрещённая тематика + документы**) и по желанию 12-й — ограничения. **Имя проекта не спрашивай** — придумай из названия продукта и покажи строкой в сводке брифа; слово «slug», папки и пути в чат не выноси (`references/brief.md` → «Имя проекта»).

**Стоп-поля** — без них дальше Шага 0 не идём и **ничего не подставляем сами**: что рекламируем, посадочная страница, регион, бюджет, цель (что считаем конверсией), тематика. Не знает — спроси ещё раз с вариантами; не ответил — блокер. Кабинет выбирается на Шаге 0.5.1, `goal_id` — на Шаге 7. Остальные поля можно закрыть дефолтом с пометкой «гипотеза» (`references/brief.md`).

**Уточнение пользователя = смена задачи.** «Не нужно X», «только Y», «это лишнее» — не повторяй
вопрос и не ставь блокер. Переключи `mode` на нужный `step:*`, сними вопросы по остальным этапам
и подтверди одной строкой: «Ок, только семантика. Нужны регион и сужающие слова». Блокер по
стоп-полю — только когда пользователь хочет полную кампанию и не отвечает.

Параллельно — прочитай сайт продукта инструментом чтения веб-страниц (например, `WebFetch`). **Если такого инструмента в среде нет или страница заблокирована** — попроси у маркетолога 5 пунктов словами: H1, бенефиты, CTA, цена/триал, доказательная база (в тот же блок брифа, в пункт 1; в режиме шага — только «что продаёте и кому»). Помечай `source: со слов клиента` и впиши в `00_brief.md` пометку о пропуске: «пропущено: нет чтения веб-страниц — данные о продукте со слов клиента, с сайтом не сверены».

**Артефакт:** `direct-campaigns/<slug>/00_brief.md` + `_state.json` (current_step, `mode`, slug, product_name; доступность Wordstat — `wordstat_mcp_available` и/или `wordstat_api_available` — дописывается на Шаге 1 после проверки, `references/mask-builder.md` → 4.3; после Шага 0.5 добавляются `direct_client_login`, `direct_account_name`, `direct_mode`, `direct_forecast_available`, `direct_server`, `metrika_server`, `metrika_counter_id`, `metrika_link`, `metrika_history_available`, `channel_history`; на любом шаге — `blockers`, см. «Блокер»).

**[GATE: маркетолог]** «Бриф верный? Идём дальше?» (в режиме шага гейта нет — сводка брифа 2–3 строки и сразу к шагу)

**[СТОП-ГЕЙТ: тематика]** Сверься с `references/ad-legal-topics.md`. Если продукт из **полностью запрещённых в РФ** тем (азартные игры, оружие, табак, рецептурные, аборты и т.д.) — пайплайн **останавливается**, кампанию в Директе не запустить: сообщи маркетологу прямо и не трать шаги на семантику. Если тема **лицензируемая** (медицина, финансы, алкоголь и т.д.) — зафиксируй в брифе, есть ли документы; без них — предупреждение и блок до предоставления. Учитывай автопредупреждения Директа (они съедают символы объявления на Шаге 8).

## Шаг 0.5. Разведка аккаунта

**Читай:** `references/mcp-account-integration.md` → Use case 0 (для 0.5.1);
`references/metrika-goals-setup.md` → «Чем работаем» и Фаза 1, п. 1–2 (для 0.5.2);
`references/channel-history.md` целиком (для 0.5.3).

Один шаг — три вопроса до любой работы с семантикой: **куда** заливаем (кабинет), **чем**
меряем (счётчик и его связка с кабинетом), **что уже было** (история рекламы клиента).
Подпункты идут подряд, без остановок: маркетолог видит **одну сводку и один гейт** в конце.
Вопрос посреди шага — только там, где без человека не решить: несколько серверов или
кабинетов, несколько счётчиков, черновики без показов.

Подпункты в `_state.json` → `current_step` пишутся **латиницей**: `recon.direct` →
`recon.metrika` → `recon.history` → `recon.done`. Возобновляешь — продолжай с первого
незакрытого.

### 0.5.1 Кабинет Директа

**Сначала найди все сервера Директа в сессии, а не первый попавшийся.** Имя сервера ничего
не гарантирует: он может называться `yandex-direct`, а может `YadLisa`, `YAD-Original`,
`yad_gossa` — ориентируйся на **набор инструментов** (`*accounts_get`, `*campaigns_add`,
`*ads_add_responsive`). Нашёл больше одного — позови `accounts_get({})` на каждом и покажи
маркетологу сводку «сервер → режим (click.ru / прямой OAuth) → кабинеты»: разные сервера
нередко ведут в один и тот же аккаунт, но бывает и наоборот, а перепутанный сервер
обнаружится только после заливки. `accounts_get({})` заодно проверяет связность MCP и
показывает режим подключения.

**Вопрос сразу** (без ответа дальше не пойти), если серверов или кабинетов больше одного:
«Какой сервер, какой кабинет?» Один сервер и один кабинет — берём молча.

Пишет в `_state.json`: `direct_client_login`, `direct_account_name`, `direct_mode`,
`direct_forecast_available`, **`direct_server`** (точное имя выбранного сервера — все
последующие шаги зовут инструменты именно его, а не «любого Директа в сессии»).

Дальше `client_login` подставляется **в каждый** вызов инструментов Директа, относящихся к
кабинету (исключение — `accounts_get`, `forecast_bids`, `dictionaries_*`: они к кабинету не
привязаны).

**Если MCP Директа нет или кабинет не виден** — ⛔ блокер «нет доступа к кабинету»: заливки
не будет. Черновик готовим дальше по ветке без live-данных; в конце маркетолог выбирает —
подключить MCP (`docs/hosted-mcp-setup.md`) и вернуться к Шагу 10 или залить файлами через
Директ Коммандер. Черновики в 0.5.3 тогда не проверяются.

### 0.5.2 Метрика и связка с кабинетом

Счётчик ищем **сразу**, а не на Шаге 7: «Метрики нет» — стоп-алярма, и узнать её лучше до
сбора семантики, а данные сайта подкармливают Шаги 1, 2, 5 и 9.

1. **Сервера Метрики — все, а не первый попавшийся.** Ищи по инструменту
   `*yandex_metrika_counters_get` (имя сервера любое — `metrikaCM`, `MetrikaDevadent`,
   `yandex-metrika`). Нашёл больше одного — позови `accounts_get({})` на **каждом**: сервер с
   пустым `accounts` в выбор не предлагай, но упомяни в сводке. Пустой `accounts_get` с
   подсказкой «не подключена Метрика» на **всех** серверах = интеграция не подключена в
   click.ru, а не «MCP нет»: попроси подключить и **обязательно дай ссылку на инструкцию**
   https://help.click.ru/97#connect-yandex-metrica — без неё маркетолог не найдёт, где это
   делается. Не жди: заведи ⛔ блокер «интеграция Метрики не подключена» и иди дальше —
   черновик готовить можно, заливать нельзя, пока блокер не снят.
2. **Счётчик — от кампаний кабинета, а не от домена.** Связку «кабинет ↔ счётчик ↔ сервер
   Метрики» проверяем **до** отчётов: иначе данные молча соберутся без расходов Директа или по
   чужому кабинету. Серверов бывает много (2 Метрики и 4 Директа у одного маркетолога —
   норма), а имена ничего не гарантируют: «парный» по названию сервер Метрики может не видеть
   счётчик, а агентский со сотней чужих счётчиков — видеть.
   - `campaigns_get({ client_login, include_strategy: true, limit: 50 })` на `direct_server` →
     `CounterIds` кампаний. У кампании их бывает 2–3 — бери тот, чей сайт совпадает с доменом
     брифа; совпадают несколько или ни один — **вопрос сразу**: «по какому счётчику считаем?».
   - Ищи этот `counter_id` на **каждом** сервере Метрики: `counters_get({ search: "<counter_id>" })`.
     Решает ответ, а не имя: сервер, который его видит, и есть `metrika_server`. Видят
     несколько — бери тот, где доступ `own` / `edit`; равны — вопрос.
   - `direct_clients_get({ counter_id })` → есть ли `chief_login`, равный `direct_client_login`.

   Итог — `metrika_link`:
   - 🟢 `green` — счётчик найден и видит кабинет: данные сайта целиком, расходы Директа из Метрики.
   - 🟡 `yellow` — счётчик найден, кабинета не видит (`clients` пустой или там другой логин):
     визиты, фразы и гео есть, **расходов и CPA Директа из Метрики нет** — их даёт
     `report_campaign` Директа на Шаге 2. Это не стоп — строка в сводке.
   - 🔴 `red` — `CounterIds` у кампаний есть, но счётчик не виден ни на одном сервере
     Метрики: интеграция Метрики в click.ru подключена не под тем аккаунтом Яндекса или не
     подключена. Ссылка https://help.click.ru/97#connect-yandex-metrica, дальше с ⛔ блокером.

   **Кампаний в кабинете нет** (новый кабинет) — связки ещё быть не может, ищи по домену:
   `counters_get({ search: "<домен из брифа>", with_goals: true })` на всех серверах. Несколько
   — вопрос «какой основной?». Не найден — проверка тега на сайте и ветки из Фазы 1 референса;
   тега нет — **алярма «Метрики нет»** и ⛔ блокер: сказать сразу, пайплайн можно вести до
   Шага 6, но до Шага 7 счётчик должен появиться.
3. **Данные сайта (если у счётчика есть трафик).** Не больше 4 коротких отчётов за 90 дней —
   `yandex_metrika_report({ counter_id, date1: "90daysAgo", limit: 50, ... })`:
   - `preset: "sources_search_phrases"` — реальные поисковые фразы, по которым приходят на
     сайт (+ `ym:s:anyGoalConversionRate`): **кандидаты в маски Шага 1** и в сужающие слова;
   - **только при `metrika_link: green`** — `preset: "sources_direct_summary"` +
     `direct_client_logins: [direct_client_login]`, метрики `ym:ad:*` расхода +
     `ym:s:goal<ID>reaches` — **фактические CPC и CPA прошлой рекламы Директа**. При `yellow`
     / `red` отчёт не строим — `ym:ad:*` придут пустыми или по чужому кабинету;
   - `dimensions: ["ym:s:regionCity"]` и `["ym:s:deviceCategory"]` с конверсией —
     **где и с чего конвертят**: вход для гео Шага 5 и корректировок Шага 9.

   Если `total_rows` мизерный или сайт новый — отчёты пропускаются без пометки, это не пробел
   среды, а факт: «данных сайта нет». Полный аудит целей — на Шаге 7, здесь не делаем.

Пишет в `_state.json`: `metrika_server`, `metrika_counter_id`, `metrika_link`,
`metrika_history_available`.

### 0.5.3 История каналов

Что прошлая реклама клиента реально дала — по каналам и по типам кампаний Директа: расход,
лиды, CPA и вывод с числами. Без этого скилл планирует «из общих знаний» и повторяет то, что у
клиента уже провалилось. Файл истории просим ещё в брифе (пункт 10); здесь — проверяем то,
что пришло.

1. **Файл `channel_history.md` / `.json` приложен** — проверь шапку (период, что считали
   лидом, пороги); чего-то нет — спроси только это.
2. **Файла нет** — попроси его один раз по шаблону из референса: что в нём должно быть и как
   его получить скиллами пакета (`yandex-direct-audit` — Директ, `yandex-metrika-manager` —
   остальные каналы) или словами. Собираешь со слов сам — **период всегда спрашивай**, лид —
   с вариантами ответа (по умолчанию ключевые цели стратегий кампаний). «Рекламы не было» →
   идём **без истории**.
3. **Черновики в кабинете** — кампании без единого показа не означают «тип не работает».
   **Вопрос сразу:** почему их не запустили; причина — в `note`.

**Пороги выводов** («мало данных», «дорого») у бизнеса бывают свои — поэтому каждый вывод
показываем с числами и порогом, на которых он построен, а не голым ярлыком.

Пишет в `_state.json`: `channel_history: file | client | none`.

### Итог шага: одна сводка, один гейт

**Артефакт:** `_channel_history.md` — единственный файл «что было до нас», две части:
- **«Каналы»** — таблица по контракту из `references/channel-history.md` (из файла аудита, со
  слов клиента или пометка «истории нет»);
- **«Сайт по данным Метрики»** — отчёты из 0.5.2 (≤ 20 строк на отчёт + 3–5 выводов) или
  строка «данных сайта нет».

**[GATE: маркетолог]** — одним сообщением:

```
Кабинет:   <логин> (сервер <имя>, click.ru)
Метрика:   счётчик 12345678 (сервер <имя>), связка с кабинетом 🟢, ~400 визитов/день
История:   2026-04..09, лид = «Заявка» + «Звонок» (из файла аудита)
           Поиск — работает: CPA 2 500 ₽ при целевом 3 000
           РСЯ — не дал лидов: 0 при 410 кликах за 62 дня (порог «мало данных»: <30 кликов / <14 дней)
           VK Ads — не дал лидов (со слов клиента: 50 тыс. ₽ за 2 месяца)
Черновики: 2 кампании без показов — не прошли модерацию
Блокеры:  нет
Всё верно? Идём к маскам?
```

Истории нет — строка «История: нет, типы кампаний и прогноз — по справочникам».

## Шаг 1. Маски

**Прочти `references/mask-builder.md` целиком.**

**Передать:** УТП и продукт из `00_brief.md`, регион, тип спроса (определяется по контексту брифа: срочный/B2B/премиум/ручная работа/массовый), доступ к Wordstat API.

**Если в `_channel_history.md` есть раздел «Сайт»** — поисковые фразы, которые уже приводят на сайт и конвертят, используй как кандидатов в колонку Б и как подсказки сужающих слов. Помечай их `source: metrika` — маркетологу на гейте видно, что маска взята из реального спроса, а не придумана.

⚠️ В отличие от старого пайплайна, **нет внешнего hypothesis-builder**. Сужающие слова берутся **напрямую из брифа**: если маркетолог дал в поле «УТП» что-то вроде «премиум», «ручная работа», «срочно» — это сужающее. Если в брифе нет явных сужающих — задай один уточняющий вопрос: «Какие 1-3 слова определяют ваш угол отстройки (например: премиум, срочно, для самозанятых)?»

**На выходе:**
- `01_masks_matrix.md` — две колонки А (сужающие) × Б (общие + товарозаменители) с языковыми и смысловыми синонимами
- `01_masks.json` — machine-readable, рабочие маски с частотностями из Wordstat
- `01_masks_wordstat_log.md` — лог итераций

**Частотности масок:** приоритет — MCP Вордстата (`wordstat_shows_batch`, все маски одним вызовом, см. `references/wordstat-mcp.md`). Сервер ищи **по инструменту `*wordstat_shows_batch`, а не по имени** — он может называться `yandex-wordstat`, `Wordstat` или как угодно ещё. Если инструмента нет ни под каким именем — фолбек `scripts.wordstat_api --mode masks` (нужен ключ Cloud Search API). Если нет ни MCP, ни ключа — **пробей частотности через браузер** пользователя (`references/wordstat-browser.md`), а не прикидкой. Пробив в браузере делает оркестратор.

**Два гейта:** (1) первая матрица — «правильное направление?», (2) после расширения и Wordstat — «финальный список?».

## Шаг 2. Прогноз частотности

В `step:semantics` шаг пропускается: бюджета нет, считать нечего.

**Прочти `references/frequency-calculator.md` целиком.** Параллельно — Use case 1–3 из `references/mcp-account-integration.md` (аудит существующих кампаний, дедупликация ключей, оценка CPC).

**Передать:** бюджет на поиск из брифа (доля поиска × общий месячный бюджет), `01_masks.json` (для прогноза CPC через MCP), регион из брифа.

**Приоритет источника CPC:** историческая AvgCpc похожих кампаний (`report_campaign`) → история (`_channel_history.md`, Шаг 0.5: фактический CPA по типам кампаний, а при связке 🟢 — CPC/CPA Директа по Метрике; по ним сверь CPA-таргет из брифа и скажи прямо, если он нереален) → живой аукцион по ключам аккаунта (`keywordbids_get`, парсит `scripts/forecast_cpc.py`) → **прогноз по своим маскам (`forecast_bids`, до 100 фраз, без создания кампании)** → справочник CPC по нишам → override от маркетолога. Сам у маркетолога CPC не спрашиваем.

⚠️ **`forecast_cpc.py` в режиме click.ru создаёт в кабинете временный черновик.** Прямого токена Яндекса нет → скрипт идёт планом Б: заводит DRAFT-кампанию `_CPC_PROBE_<дата>` с масками, снимает аукцион и удаляет её в `finally`. Кампания неактивна и денег не тратит, но это **запись в живой кабинет** — запускай скрипт **только после явного «ОК»**: «Для прогноза цены клика создам в кабинете `<логин>` временный черновик `_CPC_PROBE_<дата>` и сразу удалю. Можно?» Нет «ОК» — бери следующий источник цепочки. После запуска проверь `campaigns_get`, что черновик удалился (если скрипт упал — удалить руками).

⚠️ В режиме прокси click.ru `forecast_bids` недоступен. Есть ли в сессии второе подключение в прямом OAuth — проверь по `_state.json` → `direct_forecast_available` (Шаг 0.5): прогноз можно посчитать там, заливка останется в выбранном кабинете. Нет — справочник CPC.

**На выходе:**
- `02_frequency.md` — 3 сценария (оптимистический/базовый/пессимистический)
- `02_frequency.json` — необходимая суммарная частотность с ×2 запасом
- `_account_audit.md` (если есть похожие существующие кампании) — для контекста и предотвращения каннибализации

**Гейт:** маркетолог подтверждает или корректирует прогноз.

## Шаг 3. Расширение семантики и пробив частотностей  [делегируется субагенту `semantics`]

Расширение семантики + кластеризация (Шаг 4) вынесены в изолированный субагент `semantics` (форк): он готовит seed/anchors из `01_masks.json`, расширяет ядро одним из путей ниже, пробивает частотности через MCP `yandex-wordstat` (`wordstat_shows_batch`) или фолбеком, пишет `03_semantics_raw.csv` и сразу кластеризует его. Тысячи ключей остаются в форке.

⚠️ **Сервер `yandex-wordstat` не расширяет семантику.** Он отвечает «сколько показов у фразы», но не «какие ещё запросы бывают»: реально доступен только пакетный пробив частотностей (`wordstat_shows_batch`, до 1000 фраз за вызов). Рекурсивные волны через MCP невыполнимы — расширение берётся отдельно, а MCP пробивает частотности готового списка.

**Что готовит оркестратор:** slug из `_state.json`, регионы из брифа, необходимую суммарную частотность из `02_frequency.json` (в `step:semantics` его нет — передай `n/a`, сверку с нужной частотностью субагент пропускает).

**Как запустить субагент:** порождай дочерний агент через инструмент субагентов/форка твоей среды (например, в Claude Code — Task с `subagent_type: general-purpose`, тулы `Bash Read Write Grep Glob`), **передай ему целиком текст `subagents/semantics.md`** и три аргумента: `slug`, `регионы`, `необходимая_частотность`. Эквивалент вызова:

```
субагент semantics ← subagents/semantics.md   аргументы: <slug> "<регионы>" "<необходимая_частотность>"
```

**Субагент возвращает:** сводку ≤20 строк (собрано ключей, путь расширения `cloud-api | combinatorial | list | browser`, распределение по 4 CSV, суммарная частотность vs нужная с флагом НЕДОБОР, причина остановки) + пути к `03_semantics_raw.csv` и `04_keys_*.csv`. Сырьё CSV остаётся в форке.

**Если среда не умеет субагенты/форк** (например, Claude Desktop без инструмента Task, ChatGPT) — выполни Шаги 3-4 **инлайн** по методологии ниже. Делегирование — оптимизация, а не жёсткая зависимость. Инлайн-исполнение не считается пропуском способности и пометки о пропуске не требует: данные собираются те же, страдает только чистота контекста. Но если из-за объёма пришлось урезать сбор — это пропуск, помечай.

**Цепочка приоритетов расширения** (полная методика и правила — `references/wordstat-mcp.md` → «Шаг 3»):

1. **Скрипт Cloud Search API** (`scripts/wordstat_api.py --mode semantics`) — единственный путь с настоящей рекурсией волнами и ассоциациями; нужен ключ `WORDSTAT_API_KEY` + `WORDSTAT_FOLDER_ID` и запуск кода. Частотности — из того же ответа.
2. **Комбинаторная генерация кандидатов** из `01_masks.json` + пакетный пробив через MCP `wordstat_shows_batch` — когда есть только MCP.
3. Список от маркетолога / выгрузка сторонним сервисом + пакетный пробив.
4. Браузерный пробив (`wordstat-browser.md`) — нет ни MCP, ни ключа; делает оркестратор.

Готовим два файла в рабочей папке (нужны для пути 1):
- `_masks_seed.txt` — стартовые семена: узкие комбинации из `01_masks.json` **плюс широкие одиночные слова продукта** (база колонки Б + синонимы: `картины`, `панно`, `холст`). Широкие слова обязательны — без них волна 0 узкая.
- `_anchors.txt` — якоря-основы продуктовых слов колонки Б + синонимов (`картин`, `панно`, `холст`), по одному на строку. Запрос становится семенем следующей волны, только если содержит хотя бы один якорь — это держит рекурсию в теме продукта.

**Путь 1 (предпочтительный) — скрипт Cloud API:** та же логика рекурсивных волн готовым скриптом:
```
python -m scripts.wordstat_api --mode semantics \
    --phrases-file direct-campaigns/<slug>/_masks_seed.txt \
    --anchors-file direct-campaigns/<slug>/_anchors.txt \
    --regions <из брифа> \
    --output direct-campaigns/<slug>/03_semantics_raw.csv
```
**Профиль «Стандарт» (дефолт):** глубина 2, до 150 семян на волну, порог семени 10 показов, стоп при <5% нового, ≤1000 вызовов / ≤15 минут. **Профиль «Глубокий»** — флаги `--max-depth 3 --seeds-per-wave 300 --dry-threshold 0.03 --max-api-calls 5000 --max-minutes 40`. Профили — свойство скрипта Cloud API, а не MCP.

**Путь 2 — комбинаторика + пакетный пробив (когда есть только MCP):** сгенерируй кандидатов из `01_masks.json` (продуктовые слова × сужающие × модификаторы намерения × словоформы, 300–1000 штук), пробей пачкой `wordstat_shows_batch`, отсей `stat < 150`, сделай одну волну углубления. Детали — `references/wordstat-mcp.md` → «Шаг 3, путь 2».

Работай файлами: каждая волна/пачка дописывает `03_semantics_raw.csv`, в чат — только итог (1 строка). Тысячи ключей в контекст не тащим.

**Артефакт:** `03_semantics_raw.csv` со столбцами `keyword,frequency,mask_parent,source,depth`. Для Шага 4 значимы первые три столбца.

🚫 **Категорически нельзя** дописывать в `03_semantics_raw.csv` непробитые фразы с выдуманной частотностью. Не пробита — либо `frequency: n/a` с пометкой, либо не в файле.

**Если суммарная частотность ниже необходимой** из `02_frequency.json` — на пути 1 прогони профиль «Глубокий»; если всё равно мало — вернись на Шаг 1, расширь колонку Б (новые товарозаменители/синонимы) или попроси у маркетолога новые сужающие, и допиши их в `_masks_seed.txt` и `_anchors.txt`.

**Если расширения через Cloud API нет** (нет ключа `WORDSTAT_API_KEY` / нет запуска кода) — иди по путям 2–4. Через MCP расширения семантики не будет, только пробив частотностей; в `03_semantics_raw.csv` и в сводке Шага 3 — **обязательная пометка о пропуске**: «пропущено: нет расширения семантики через Вордстат — сервер `yandex-wordstat` умеет только частотности; ядро построено комбинаторикой масок и пробито пакетно. Как добрать: прогнать `scripts/wordstat_api.py --mode semantics` с ключом Yandex Cloud или прислать ядро сторонним сервисом списком».

**Если Wordstat недоступен вообще** (инструмента `*wordstat_shows_batch` нет ни под одним именем сервера — проверь, а не полагайся на имя `yandex-wordstat`; и `python -m scripts.wordstat_api --check` упал / нет ключа / среда не запускает код) — переходим на **браузерный пробив частотностей** (путь 4) через сессию пользователя (`references/wordstat-browser.md`). Пробив делает **оркестратор** (у форка `semantics` браузерных инструментов нет). Непробитые фразы помечаем `frequency: n/a` — прикидку не подставляем. Дальше расчёт бюджета Шага 2 считается непроверенным — так и скажи на гейте.

## Шаг 4. Кластеризация (НЧ/СЧ/ВЧ + Поиск/РСЯ)  [делегируется субагенту `semantics`]

4 CSV ниже уже произведены субагентом `semantics` (Шаг 3) — он кластеризует `03_semantics_raw.csv` по этому же reference. Если субагент недоступен, оркестратор делает это инлайн. В любом случае **гейт и решение — здесь, в основном контексте**.

**Прочти `references/wordstat-filter.md` целиком** (нужно оркестратору для проверки распределения на гейте и для инлайн-фолбека).

**Передать:** `03_semantics_raw.csv`, тип ниши; каналы (Поиск/РСЯ/оба) и бюджет (для решения по ВЧ) — если есть в `_state.json` / брифе. Нет — каналы оба, ВЧ на Поиск не берём (`vch_no_budget`), пометка в `04_wordstat_filter_summary.md`.

**На выходе (4 CSV + summary):**
- `04_keys_search_obligatory.csv` — НЧ + СЧ для Поиска (база)
- `04_keys_search_vch.csv` — ВЧ для Поиска (только при бюджете ≥100к + массовый продукт + бренд)
- `04_keys_rsya.csv` — СЧ + ВЧ для РСЯ
- `04_keys_excluded.csv` — отброшенные с причиной
- `04_wordstat_filter_summary.md` — отчёт + гейт

**Пороги:** НЧ <350, СЧ 350-750, ВЧ >750 показов/мес. Минимум 150 показов/мес — иначе отбрасываем.

**Гейт:** маркетолог подтверждает распределение или корректирует.

## Шаг 5. Структура кампаний

**Прочти `references/campaign-structure.md`.**

### Шаг 5.1. Построение структуры

Модель объявлений одна — **комбинаторное объявление в ЕПК** (классическая ТГО-модель убрана: ТГО заморожены с 30.06, автомиграция ТГО→комбинаторное с 14.07 — их технически не создать). Гейта выбора модели больше нет.

Строй структуру по разделу «Модель структуры (комбинаторная ЕПК)» в `references/campaign-structure.md`:
- **По умолчанию:** 1 угол/сегмент → 1 РК; Поиск+РСЯ+Галерея+Карты обслуживаются одной РК — не разделяем. Групп в РК по умолчанию одна, отдельная группа — только под другой лендинг или ВЧ (правило — `campaign-structure.md` → «Группы внутри кампании»); в каждой группе одно комбинаторное объявление.
- **Если нужна раздельность Поиск/РСЯ** — экспертные варианты ЕПК «только Поиск» / «только РСЯ» (а не отдельные классические кампании).
- Углы/сегменты (Премиум, Срочно, Бренд, B2B) по-прежнему разносятся по отдельным РК.
- **Автотаргетинг на Поиске обязателен** (в ЕПК его нельзя выключить, только ограничить категориями) и может дать половину трафика группы и больше — особенно на узкой семантике. Директ заводит его в каждой группе ЕПК сам и **со всем включённым** — пять категорий и все брендовые запросы, включая конкурентов. Поэтому в структуре у каждой РК задаём **осознанно**: `autotargeting_categories` (какие категории запросов разрешены), `autotargeting_brands` (какие брендовые запросы) и — только для ручных ставок в РК «только Поиск» — `autotargeting_bid`. При автостратегии ставка и приоритет на автотаргетинг не действуют, рычаг один — категории и бренды. По умолчанию предлагаем: целевые + узкие, без брендов + свой бренд; на узкой семантике (<30 фраз в группе) добавляем альтернативные; брендовая РК — только целевые и только свой бренд; бренды конкурентов — только по прямой просьбе брифа. Решение показываем на гейте Шага 5. MCP применяет его на Шаге 10Б (`autotargeting_set`). Детали — `references/campaign-structure.md`.

Жёсткого потолка на число кампаний нет — см. «Много кампаний — мягкая подсказка» в референсе.

**История (`_channel_history.md`, Шаг 0.5).** Тип кампании, у которого в истории
`no_leads` или `expensive`, попал в структуру — **[GATE: маркетолог]** с числами и порогом:
«РСЯ: 80 тыс. ₽, 410 кликов, 0 лидов за полгода. Всё равно закладываем? Что меняем по
сравнению с прошлым разом?» Тип молча не убираем — решение за маркетологом, но с цифрами на
столе. `no_data` — «данных мало, вывода нет»; `never_ran` — вывода нет, учитываем причину из
`note`. Истории нет (`channel_history: none`) — у каждого типа в `05_campaign_structure.md`
пометка **«без истории»**.

**Есть раздел «Сайт» в `_channel_history.md`** — сверь гео брифа с городами, откуда реально конвертят: если 70 % конверсий из одного города, а бриф просит «вся Россия», предложи отдельную РК или корректировку, а не молча следуй брифу. Ретаргет-кампания возможна, только если у счётчика есть трафик (сегменты строятся на Шаге 7).

**Артефакт:** `05_campaign_structure.md` — таблица с колонками `#`, `Кампания`, `Тип`, `Угол`, `Бюджет`, `Группы` + machine-readable `05_campaign_structure.json` для следующих шагов.

**[GATE: маркетолог]** «Эту схему берём? Не объединять ли что?»

## Шаг 6. Минус-слова и кросс-минусация

**Прочти `references/negative-keywords-builder.md` целиком.**

**Передать:** `05_campaign_structure.json`, ключи из всех CSV Шага 4, сужающие слова кампаний, тип ниши.

**На выходе (7 артефактов):**
- `06_negative_keywords_account.csv` — аккаунт-уровень
- `06_negative_keywords_search.csv` — Поиск-уровень
- `06_negative_keywords_rsya.csv` — РСЯ-уровень (короткий)
- `06_negative_keywords_cross.csv` — кросс-минусы по кампаниям
- `06_cross_minus_matrix.md` — наглядная матрица
- `06_negative_keywords.json` — machine-readable, идёт в MCP-заливку
- `06_negative_summary.md` — сводка

**Гейт:** маркетолог подтверждает 3-уровневое распределение + кросс-матрицу.

## Шаг 7. Метрика и цели

**Прочти `references/metrika-goals-setup.md` целиком.**

**Передать:** `counter_id`, сервер Метрики и `metrika_link` из `_state.json` (Шаг 0.5.2), URL сайта и цель в Метрике из брифа, `05_campaign_structure.json`, тип ниши, `direct_client_login`.

**Основной путь — MCP Метрики**, токен Метрики не нужен:
1. **Аудит по факту, а не по названиям:** `goals_get({ counter_id, stats_days: 30 })` → у каждой цели достижения и конверсия за 30 дней. Мёртвые (0 достижений), мусорные (конверсия 30 %+) и пригодные — с конверсиями в неделю. `direct_clients_get` — видит ли счётчик наш кабинет Директа.
2. **План целей** по шаблону ниши. Сначала цели без разработчика (`url`, `phone`, `messenger`, `file`, `visit_duration`), `action` (JS) — только когда иначе никак. Для ретаргет-кампании — 1–3 сегмента (`segments_get` → есть ли уже; новые — с проверкой выражения отчётом).
3. **Предпросмотр:** каждую новую цель и сегмент — через `goals_add` / `segments_add` с `dry_run: true`.
4. **[GATE: маркетолог]** подтверждает план **до** создания.
5. **Создание** без `dry_run`, `goal_id` — в лог сразу; финальная сверка `goals_get`. **Скилл ничего не удаляет в Метрике** — мусорные цели просто не используем.

**Фолбек:** MCP Метрики нет или интеграция не подключена в click.ru (в последнем случае — сначала ссылка на инструкцию https://help.click.ru/97#connect-yandex-metrica) → Management API с OAuth-токеном (scope `metrika-manage`) → режим «инструкции для UI». Пометка о пропуске — обязательна. В режиме «инструкции для UI» маркетолог заводит цель руками и присылает `goal_id`; пока его нет — ⛔ блокер «нет цели для оптимизации», заливки не будет.

**На выходе:**
- `07_metrika_audit.md` — состояние счётчика и таблица целей с достижениями за 30 дней и вердиктом
- `07_metrika_goals_plan.md` — план новых целей и сегментов (до создания)
- `07_metrika_setup_log.md` — лог вызовов (dry-run и боевые)
- `07_metrika_goals.json` — `counter_id`, `goal_ids`, `conversions_per_week`, `segments`, привязка к кампаниям

**Алярма:** если Метрики на сайте нет (или визитов ноль — код снят) — стоп пайплайна (⛔ блокер), маркетолог сначала устанавливает/чинит счётчик. Цель для оптимизации выбирает **маркетолог** на гейте: скилл предлагает, но не назначает цель по похожему названию.

После — привязка к кампаниям на Шаге 10: `counter_ids` и `priority_goals` передаются прямо в `campaigns_add` (или в `campaigns_update` для уже созданной кампании). Ценность конверсии для `priority_goals` берётся из брифа; её нет — передаём только `counter_ids`, цели не выдумываем.

## Шаг 8. Объявления

**Обязательно прочитай** `references/yandex-direct-specs.md` и `references/ad-copywriting.md` **до** написания.

Модель одна — **одно комбинаторное объявление на группу**; групп в РК столько, сколько задано на Шаге 5 (по умолчанию одна). См. `ad-copywriting.md` → «Комбинаторное объявление» и лимиты в `yandex-direct-specs.md`:
- **3-7 заголовков** (≤56 симв., минимум 2 коротких ≤35), обязательное вхождение ключа хотя бы в один. **Отдельного «Заголовка 2» в комбинаторном нет** — есть набор равноправных заголовков с разными углами;
- **2-3 текста** (≤81 симв. без «узких» знаков `! , . ; : "`, их — до 15), каждый самодостаточен;
- картинки (до 5) из ассетов брифа — нет ассетов — без них, TODO в Шаг 8.5. Видео (до 6 в лимите Директа) через MCP не заливаются: файлы маркетолога уходят в `10_launch_log.md` ручной работой;
- быстрые ссылки (4-8; заголовки 1–4 вместе ≤66 симв., 5–8 тоже), уточнения (2-4), отображаемая ссылка;
- **Минусы группы** — узкие, только для этой группы;
- **опциональные элементы роста CTR** (если есть данные в брифе): кнопка действия, цена, промоакция (до +30% CTR), карусель 2–10 изображений — см. `ad-copywriting.md` → «Дополнительные элементы». Цену/акцию/промокод берём только реальные, не выдумываем.

Для лицензируемых тем сверься с `references/ad-legal-topics.md`: Директ вставит автопредупреждения (№334), которые съедают символы — не рассчитывай на полный лимит заголовка/текста.

Артефакт `08_creatives.json` в форме ЕПК (в группе объект `combined_ad`) — схема `assets/creatives_schema_combined_example.json`.

**Финальная проверка через MCP:**
1. Дедупликация ключей через `keywords_get` (Use case 2 в `mcp-account-integration.md`)
2. Прогноз CPC через `report_campaign` с `campaign_ids` похожих кампаний (из `_account_audit.md`) и `date_range: LAST_30_DAYS`; **без `campaign_ids` вызов упадёт**. Иначе берём из `02_frequency.json`

**Артефакты:** `08_creatives.md` (для маркетолога) + `08_creatives.json` (machine-readable). Схема: `assets/creatives_schema_combined_example.json`.

После — `python -m scripts.preflight --workspace <path>`: проверка лимитов комбинаторного (заголовки, тексты с учётом «узких» знаков, длины слов, символы отображаемой ссылки, быстрые ссылки и их суммарная длина по четвёркам, уточнения, наличие файлов картинок, объём минус-фраз). Затем `python -m scripts.generate_ads_xlsx --workspace <path>` → xlsx/csv + `warnings.txt` для маркетолога.

**Если среда не запускает код** — отдай обе команды пользователю текстом и попроси прислать вывод. Не может и он — сверь лимиты вручную по `references/yandex-direct-specs.md` (пройди тот же перечень: заголовки, тексты, длины слов, ссылки, уточнения, объём минус-фраз) и впиши в `08_creatives.md` пометку: «пропущено: нет запуска кода — `preflight.py` не прогонялся, лимиты сверены вручную. Потеряно: машинная проверка перед заливкой. Как добрать: прогнать `python -m scripts.preflight` в среде с Python». Ручная сверка **не отменяет** гейт: объявление с нарушенным лимитом Директ отклонит.

**[GATE: маркетолог]** «Объявления принимаешь?»

## Шаг 8.5. Картинки для РСЯ (приём от маркетолога или генерация)

**Прочти `references/rsya-creatives.md` и `references/visual-generation.md` целиком.**

Скилл работает только с **картинками**: их `adimages_add` заливает, а видео MCP создать нечем (`video_extension_ids` взять негде — `references/yandex-direct-mcp.md` → «Чего MCP не умеет»). Видео маркетолога, если оно есть, не генерируем и не заливаем — пишем файлы списком ручной работы в `10_launch_log.md`.

Если в брифе только Поиск — шаг пропускается. Если РСЯ есть:

0. **Источник визуалов — сперва спроси, потом генерируй.** Источники по порядку,
   берётся первый доступный: свои картинки маркетолога → инструмент `create_ad_image`,
   если он есть в среде → скрипт `generate_creative_images.py` с ключом OpenAI → ТЗ
   дизайнеру. Источник выбирай по инструментам, которые у тебя есть, а не по догадке о
   среде: один текст работает и в Claude Desktop/Code, и на платформе AI Hub. Приоритет — за маркетологом:
   - **Свои креативы маркетолога** (поле «существующие материалы» брифа или прямо
     сейчас). Спроси: «Есть свои картинки под РСЯ? Пришли файлы или публичные ссылки —
     возьму их, иначе сгенерирую». Присланные **файлы** сложи в `assets/images/`,
     присланные **публичные ссылки** запиши в `08_creatives.json` группам как есть
     (в Шаге 10Б они пойдут в `adimages_add` напрямую, без KeepImage). Прогони
     `python -m scripts.validate_assets --workspace <path>` — не прошедшие
     техтребования Директа Директ отклонит (`5004`), верни маркетологу на замену.
     Свои картинки закрывают потребность группы — генерацию для неё пропусти.
   - **Нет своих, есть `create_ad_image`** — генерируй им по блоку «Генерация
     инструментом» ниже; пункты 1 (ключи), 3–6 пропусти.
   - **Нет своих и нет `create_ad_image`** — генерируй скриптом по пунктам 1–7.
   - **Нет ни своих, ни `create_ad_image`, ни ключей на генерацию** — фолбек-ветка в конце шага (ТЗ дизайнеру).

   **Генерация инструментом `create_ad_image`.** Ищи его по имени, без префикса сервера
   (на платформе AI Hub — `mcp__aihub__create_ad_image`); если инструменты подгружаются
   поиском — поищи (правило «Отсутствие инструмента в списке…» выше). Есть — ключ OpenAI
   не спрашивай, скрипты генерации и валидации не запускай. `brand.json` и концепции —
   как в пунктах 1–2, с гейтом.
   - Один вызов — одна картинка: `platform: "direct"`, `format` — `1x1` (1080×1080),
     `4x3` (1440×1080), `16x9` (1920×1080), `3x4` (1080×1440). Набор форматов — как у
     скрипта: первой концепции 4x3, 16x9, 1x1; остальным 4x3, 16x9; не больше 5 на группу.
   - `file` — `assets/images/g{N}_{CID}_{FMT}.jpg` (`references/visual-generation.md`);
     пути пиши в `combined_ad.images` в `08_creatives.json`.
   - `scene` — сцена концепции по-английски, до 2000 символов: что в кадре, свет, ракурс.
     Цвета бренда называй словами в сцене, hex из `brand.json` — в `colors` (до 6),
     стиль — в `style`. Сцены сохрани в `assets/prompts/g{N}_{CID}_{FMT}.txt`.
   - Текста, логотипов и цен на картинке не будет: УТП (`usp_on_creative`) покажи
     сценой, а словами — в заголовке и тексте объявления. Не пиши в сцену то, что
     запрещает модерация: деньги, алкоголь, табак, оружие, болезни, «до/после»,
     узнаваемых людей и бренды.
   - Порядок вместо dry-run и теста: одна картинка концепции A → **[GATE]** → после
     согласия остальные. Картинки уже в формате Директа — `validate_assets.py` не нужен.
     Каждый вызов платный для пользователя: без причины не перерисовывай.
   - **Не `ok`.** `unavailable` — генерация в среде не настроена, не повторяй; скрипт
     OpenAI — только если есть запуск кода, иначе свои картинки или ТЗ дизайнеру.
     `error` — прочитай текст: отказ модели по описанию — упрости сцену (без
     узнаваемых людей, брендов и спорных деталей); перегружено, не успела, не удалось —
     повтори тот же вызов. Повторов на картинку — **не больше одного**, параметры
     наугад не перебирай. **Вторая неудача** — остановись и скажи пользователю, какая
     картинка не вышла, **причину словами из ответа** и варианты: позже, свои картинки
     или ссылки, ТЗ дизайнеру. Не говори «генерация недоступна» без причины из ответа и
     не уходи вместо картинок к другим шагам — их делай, когда пользователь выберет.

1. **Pre-flight:** собери `brand.json` в рабочей папке из брифа (product_name, цвета
   HEX, style_references). Проверь ключи: `python -m scripts.manage_credentials list`.
   Нет ключа openai — попроси пользователя прислать в чат и сохрани:
   `python -m scripts.manage_credentials set openai --key <KEY>`. Ключ НИКОГДА
   не пишем в артефакты рабочей папки.
2. **Концепции:** добавь в `08_creatives.json` каждой группе
   `combined_ad.visual_concepts` (2 концепции: id A/B, type, angle, usp_on_creative —
   УТП из Шага 1, углы из гипотез). **[GATE: маркетолог подтверждает концепции]**
3. **Dry-run:** `python -m scripts.generate_creative_images --workspace <path> --dry-run`
   → прочитай 1–2 промпта из `assets/prompts/`.
4. **Тест:** `... --concept A` → одна картинка. **[GATE: нравится? иначе — правь
   концепцию/шаблон и --force]**
5. **Полный прогон:** `python -m scripts.generate_creative_images --workspace <path>`
   → до 5 картинок на группу (A×3 форматов + B×2).
6. **Валидация:** `python -m scripts.validate_assets --workspace <path>` — все ассеты
   должны пройти (450–5000 px, соотношения, ≤10/100 МБ).
7. **[GATE: маркетолог смотрит assets/images перед заливкой]**

**Фолбек без `create_ad_image` и без ключей:** собери `08_5_rsya_creatives_TODO.md` (ТЗ дизайнеру по
`references/rsya-creatives.md`), кампания заливается без визуалов, TODO в launch log.

**Артефакты:** `assets/images/*.png` (скрипт) или `*.jpg` (`create_ad_image`), `assets/prompts/`,
`assets/generation_log.json`, `assets/validation_report.json`; обновлённый
`08_creatives.json` (`visual_concepts`, `images`).

## Шаг 9. Стратегия торгов

**Прочти `references/bidding-strategy.md` целиком** (подраздел «Комбинаторная ЕПК — одна стратегия на РК»).

В комбинаторной ЕПК Поиск и РСЯ — одна кампания, поэтому **одна стратегия на РК**; `channel` в `09_bidding_strategy.json` переносится из `05_campaign_structure.json`.

**Сетевая сторона — по `channel` РК из `05_campaign_structure.json`** (таблица — `bidding-strategy.md` → «Сочетания сторон в ЕПК»):
- `epk` (поиск + РСЯ) — автостратегия с бюджетом на поиске, `network_strategy: NETWORK_DEFAULT`;
- `epk-search` (только поиск) — автостратегия или ручные на поиске, `network_strategy: SERVING_OFF`;
- `epk-network` (только РСЯ) — `search_strategy: SERVING_OFF`, автостратегия с `weekly_spend_limit` в сети.

`SERVING_OFF` в сети — только когда РСЯ в структуре этой РК нет. Одинаковая автостратегия на обеих сторонах — ошибка `4000`. Ручные ставки (`HIGHEST_POSITION`) выключают РСЯ, поэтому для `epk` их не ставим.

**Бюджет — недельный** (базовая единица 2026, мин. 300 ₽/нед); дневной — только для ручных ставок на Поиске, **тоже от 300 ₽**. **Если РК несколько по разным углам с одной целью** — рассмотри **пакетную стратегию** (общий пул обучения: порог ≥10 конв/нед считается суммарно по пулу, до 100 РК) — это спасает мелкие/премиум РК от «голода» по конверсиям. Детали — в `bidding-strategy.md`.

**Передать:** `05_campaign_structure.json` (кампании + бюджеты), целевые CPC из `02_frequency.json`, целевые CPL/CPA из брифа, `07_metrika_goals.json` (критичный вход — `goal_id` для оптимизации в Фазе 3 **и `conversions_per_week` по каждой цели**), `_channel_history.md` (раздел «Сайт», если есть — устройства и гео для корректировок), состояние коллтрекинга (из брифа), тип ниши.

**Объём конверсий — из Метрики, а не на глаз.** Если у цели сайта уже ≥10 конв/нед со всего трафика, а РК получает заметную его долю — Фаза 3 достижима быстро, скажи это цифрами. Если у макро-цели 1–3 конв/нед — честно: на неё кампания не обучится, предложи оптимизацию на более частую цель воронки или пакетную стратегию. Цель типа `action`, для которой разработчик ещё не поставил событие (0 достижений), целью оптимизации **не** ставим.

**На выходе:**
- `09_bidding_strategy.md` — план с 3-фазной эволюцией (Старт → Промежуточная → Масштаб) и триггерами переключения
- `09_bidding_strategy.json` — machine-readable для заливки: каждая фаза описана **параметрами API** (`search_strategy` / `network_strategy` с типом и обязательными полями), а не свободным текстом. Таблица соответствия — в `bidding-strategy.md` → «Маппинг в API»
- `09_bidding_monitoring.md` — что и когда мониторить

**Заливается только Фаза 1.** Она уходит через MCP вместе с кампанией на Шаге 10 — доделывать руками её не нужно. Фазы 2–3 и триггеры переключения — это **план для маркетолога**, а не работа скилла: скилл кампанию не ведёт и не оптимизирует, он отдаёт условия перехода цифрами в `09_bidding_strategy.md` и `09_bidding_monitoring.md`. Скажи об этом на гейте словами, чтобы не осталось впечатления, что переключение произойдёт само. Пакетную стратегию MCP не умеет: если она рекомендована, пункт уходит в `10_launch_log.md`.

**Главное правило:** на старте — **НЕ** «Максимум конверсий». «Максимум кликов» (с ограничением средней цены клика) 2 недели → ужесточить ограничение средней цены клика → (после 10+ конверсий/нед, для приложений 70/нед) → «Максимум конверсий» с оплатой за конверсии и ограничением CPA.

**Алярма:** если коллтрекинг не настроен (важно для срочного спроса и B2B) → Фаза 3 заблокирована, предупреждение до запуска.

**[GATE: маркетолог]** подтверждает стратегию по каждой кампании.

## Шаг 10. Заливка через MCP + финальные артефакты

### 10А. Финальный чек-лист готовности

Перед заливкой пройдись по ВСЕМ артефактам:

```
✅ 00_brief.md
✅ 01_masks.json
✅ 02_frequency.json
✅ 03_semantics_raw.csv
✅ 04_keys_search_obligatory.csv (+ keys_search_vch.csv если применимо)
✅ 04_keys_rsya.csv (если есть РСЯ)
✅ 05_campaign_structure.json
✅ 06_negative_keywords.json
✅ 07_metrika_goals.json
✅ 08_creatives.json
✅ 09_bidding_strategy.json
✅ region_ids в 05_campaign_structure.json (Use case 4 в mcp-account-integration.md)
```

**Метрика перед заливкой (если есть MCP Метрики):** `goals_get({ counter_id })` — все `goal_id` из `07_metrika_goals.json` существуют и не удалены; `report_bytime({ counter_id, metrics: ["ym:s:visits"], date1: "3daysAgo" })` — визиты идут. Цель пропала или визитов ноль — заливку не начинаем, возвращаемся на Шаг 7.

**Финальная проверка перед заливкой.** Каждая строка получает ✅, ⛔ или 👀 (проверить
руками) и целиком показывается маркетологу в предпросмотре 10Б:

| Что | Как проверяем | ⛔ если |
|---|---|---|
| Кабинет | `client_login`, название и сервер из `_state.json` = кабинет, подтверждённый на Шаге 0.5.1; `accounts_get` отвечает | кабинет не виден, ошибка авторизации |
| Кампания | имя новой кампании не совпадает с существующими в кабинете (`campaigns_get`); не каннибализирует действующие (Use case 1–2 `mcp-account-integration.md`) | дубль имени — переименовать или явно подтвердить |
| Посадочная | URL в объявлениях и быстрых ссылках = посадочная из брифа; страница открывается (чтение веб-страниц) | 404, редирект на чужой домен. Читать страницы нечем — 👀 |
| Бюджет | недельный лимит × 4,3 ≈ месячный бюджет брифа (±10 %); сумма по кампаниям не больше брифа | больше брифа |
| Цель Метрики | `goal_id` подтверждён на Шаге 7, существует и срабатывает (проверка выше) | нет подтверждённой цели |
| Минус-слова | `06_negative_keywords.json` не пуст; минусы кампании ≤ 20 000 символов; кросс-минусация применена | пусто или лимит превышен |
| Юр. риски лендинга | `references/ad-legal-topics.md` → «Проверка посадочной страницы» | лицензируемая тема, а сведений о документах на сайте нет. Остальные риски — 👀 |

Хоть одна ⛔ или открытый блокер из `_state.json` → `blockers` — **заливки нет**. 👀 не
блокирует, но уходит в `10_launch_log.md` → «Проверить руками».

Хоть один артефакт отсутствует — **не запускай заливку**. Вернись на соответствующий шаг.

### 10Б. Заливка через MCP

**Полная последовательность — `references/mcp-account-integration.md`, Use case 5. Здесь только каркас, не дублируй логику.**

MCP заливает всю ЕПК целиком: оболочку кампании (`campaigns_add` с `campaign_type: "UNIFIED"` и обеими сторонами стратегии из `09_bidding_strategy.json`), группы (`adgroups_add` с `group_type: "UNIFIED"`, по одной на каждую из `08_creatives.json`), комбинаторное объявление в каждой (`ads_add_responsive`), быстрые ссылки, уточнения, отображаемую ссылку, ключи, ставки, автотаргетинг (`autotargeting_set` — категории и бренды из `05_campaign_structure.json`) и корректировки.

1. **Pre-flight:** `python -m scripts.preflight --workspace <path>`. Есть ошибки — заливка не стартует. Среда не запускает код — ручная сверка по `references/yandex-direct-specs.md` (см. ветку в Шаге 8) плюс пометка о пропуске; блокирующая роль гейта сохраняется.
2. **Предпросмотр:** весь конвейер с `dry_run: true` — инструменты вернут тела запросов, ничего не создавая, но выполнив все проверки. Покажи маркетологу сводку **вместе с финальной проверкой 10А** (кабинет, посадочная, юр. риски — тоже) и дождись явного «ОК». Любая запись в кабинет — только после него.
3. **Заливка** по порядку: ассеты (картинки → быстрые ссылки → уточнения) → кампания → группа → объявление → ключи → ставки → автотаргетинг (строго после ставок: групповая ставка ложится и на него) → корректировки. ID каждого объекта пиши в `10_launch_log.md` **сразу**, а не в конце: при обрыве заливка продолжается с места, повторный `campaigns_add` создаст дубль. ⚠️ **Но 19-значные ID сейчас приходят от MCP округлёнными** (`references/yandex-direct-mcp.md` → «Конверт ответа»), поэтому идемпотентность по записанному ID **ненадёжна**: при обрыве и повторном проходе проверяй, что уже создано, через `campaigns_get` по имени/дате, а не полагайся только на ID из лога.
4. **Проверяй ответы целиком.** Ответ может быть `success: true` и при этом содержать отклонённые объекты — «вернулось без ошибки» не значит «всё создано».

**Картинки — через KeepImage → `image_url`.** Хостовый MCP Директа не видит локальные файлы, но `adimages_add` теперь принимает `image_url` — публичную ссылку http/https, сервер Директа скачивает файл сам (сверено живьём 03.09.2026). Мост «локальный файл → публичная ссылка» — временное хранилище **KeepImage** (ссылка без авторизации, живёт ≤2 ч; подключение — `docs/hosted-mcp-setup.md`, справочник по `adimages_add` — `references/yandex-direct-mcp.md`). Ссылка нужна живой только на момент вызова: Директ скачивает картинку и сохраняет у себя, дальше ссылка не нужна — TTL не мешает.

**Исключение — среда, где `adimages_add` принимает путь к файлу** (так на платформе AI Hub: её инструкции говорят передавать путь в `image_url`). Там шаги 1 и 4 ниже пропусти: KeepImage, токен click.ru и `upload_creatives_to_storage.py` не нужны. Передавай путь из рабочей папки: `adimages_add({ client_login, image_url: "assets/images/g1_A_4x3.jpg", name })`; картинкам `create_ad_image` `crop` не нужен — они уже в формате Директа. В Claude Desktop/Code хостовый MCP Директа локальные пути не читает — там порядок ниже.

Порядок на каждую картинку из `combined_ad.images`:

1. **Локальный файл → публичная ссылка.** Приоритет — скрипт: `python -m scripts.upload_creatives_to_storage --workspace <path>` (постит файлы в KeepImage по HTTP, пишет `assets/storage_manifest.json` — соответствие путь → ссылка). Среда без запуска кода — MCP хранилища: инструмент `*storage_publish_image(data_base64=<файл>)` по одной картинке (сервер ищи по инструменту, имя может быть любым — `KeepImage`, `storage`, …) (дорого и ненадёжно для крупных до 10 МБ — base64 рвётся; ужимай или отдавай публичной ссылкой). Картинка уже по публичному URL (маркетолог дал ссылкой) — KeepImage не нужен, шаг 1 пропускается.
2. **Ссылка → хеш.** `adimages_add({ client_login: <из _state.json>, image_url: <ссылка из манифеста>, name: "<имя>", crop: "square"|"wide" })` → `AdImageHash`. По одному вызову на файл, до 5. Вызовы **строго последовательные** — параллельная загрузка на MCP даёт ошибку `waiting on finished stream`.
3. **Хеши → объявление.** Собери `AdImageHash` в `image_hashes` и передай в `ads_add_responsive`. Пиши хеши в `10_launch_log.md` сразу — при обрыве не грузить повторно.
4. **Прибери.** После получения всех хешей ссылки в KeepImage не нужны — `storage_delete(id)` по манифесту (или дай истечь за ≤2 ч).

⚠️ Картинка должна проходить техтребования Директа (Шаг 8.5, `scripts/validate_assets.py`): иначе `adimages_add` вернёт `5004 Invalid image file type` — это отказ **Директа по контенту**, а не проблема KeepImage. **Фолбек** (нет KeepImage и нет запуска кода): объявление заливается **без** `image_hashes`, добавление картинок уходит в `10_launch_log.md` списком ручной работы (интерфейс Директа или Коммандер) с пометкой о пропуске. Остальная цепочка на хостовом MCP работает как обычно.

Кампании остаются в **DRAFT**.

**Что MCP не умеет** — уходит в `10_launch_log.md` честным списком: Библиотека минус-фраз при переполнении лимита кампании, пакетная стратегия, временной таргетинг, кнопка действия/цена/промоакция/карусель, видео, условия ретаргетинга (сегменты Метрики из `07_metrika_goals.json` → `segments` уже созданы — в лог пиши их `segment_id`, чтобы маркетолог собрал из них условие руками). Полный перечень — в `references/yandex-direct-mcp.md` → «Чего MCP не умеет».

**Фолбек — Директ Коммандер.** Если MCP не подключён и `scripts/setup_yandex_direct_mcp.py` не помог, либо маркетологу нужен визуальный ревью структуры до отправки: импорт `ads.xlsx`/`keywords.xlsx` Шага 8 по `references/direct-commander-import.md`.

### 10В. Финальные артефакты для маркетолога

После заливки:

1. `python -m scripts.generate_media_plan --workspace <path>` → `media_plan.docx` (агрегирует все артефакты пайплайна)
2. `python -m scripts.generate_ads_xlsx --workspace <path>` → `ads.xlsx` + `keywords.xlsx` + `warnings.txt`
3. `10_launch_log.md` — итоговый отчёт для маркетолога. Разделы **обязательны** в этом порядке:
   - **«Итог»** — 3–5 строк: что залито (кампании, группы, объявления, ключи — с ID), в каком
     статусе (DRAFT), что осталось до активации.
   - **«Сделал сам»** — по строке на шаг: шаг → артефакт → ✅ / ✅ с пометкой о пропуске.
   - **«Данные и источники»** — откуда взято каждое ключевое решение: регион, бюджет, цель,
     CPC/CPA, типы кампаний, ключи. Источник — один из: бриф / кабинет Директа / Метрика /
     история каналов / Вордстат / справочник / гипотеза (с пометкой «требует подтверждения»).
   - **«Блокеры»** — какие были, как сняты; неснятые — первой строкой отчёта.
   - **«Пропущено из-за среды»** — см. ниже.
   - **«Проверить руками»** — всё 👀 из финальной проверки 10А (юр. риски лендинга и т. п.), что
     MCP не умеет (условия ретаргетинга…), что Директ сохранил, но не применяет (`not_in_effect`,
     предупреждения 10160/10161), ОРД-маркировка, активация.

**Если среда не запускает код** — оба генератора пропускаются: медиаплан собери прозой прямо в `media_plan.md` по тем же артефактам, таблицу объявлений — таблицей Markdown в `ads.md`. Пометка: «пропущено: нет запуска кода — `media_plan.docx` и `ads.xlsx` не собраны, вместо них `.md`. Как добрать: прогнать `scripts/generate_media_plan.py` и `scripts/generate_ads_xlsx.py` в среде с Python». Для импорта в Директ Коммандер нужен именно xlsx или csv — без запуска кода этот путь фолбека закрыт, скажи об этом прямо.

**Обязательный раздел `10_launch_log.md` — «Пропущено из-за среды».** Собери в него все пометки о пропуске со всех шагов одним списком: какой шаг, какой способности не было, что сделано вместо, что недобрано, что делать человеку. Пусто — напиши «пропусков нет, все шаги выполнены полностью». Этот раздел маркетолог читает **до** активации кампании: он показывает, на каких данных построена кампания, а на каких — нет.

Покажи файлы пользователю тем способом, который есть в среде (например, `computer://`-ссылками): Media plan, Ads, Keywords, Launch log. Способа показать нет — перечисли пути к файлам от корня рабочей папки.

### КРИТИЧНО: ИИ НИКОГДА не активирует кампании сам

Кампании остаются в **DRAFT**. Финальный клик «Активировать» (`campaigns_resume`) делает только маркетолог в интерфейсе Директа.

Активация = реальный расход бюджета. Нужен последний человеческий чек: посадочные открываются, Метрика работает (визиты и цели проверены на 10А — напомни, что JS-цели от разработчика проверяются только после его деплоя), коллтрекинг подключён, бюджеты налиты, ОРД-маркировка включена.

---

# Стиль работы

- **Не клянчи.** Один прямой вопрос за раз. В Шаге 0 — единый блок брифа.
- **Новая просьба важнее плана.** Сообщение пользователя посреди работы — сначала ответ на
  него, а не следующий шаг из `_state.json`. Просьбу, которую скилл выполнить не может,
  закрывает прямой отказ (раздел «Чего скилл не делает»), после него — стоп.
- **Уточнение = смена задачи.** «Не нужно X / только Y / это лишнее» — не повторяй вопрос и не
  ставь блокер: переключи `mode` на нужный `step:*`, сними вопросы по остальным этапам,
  подтверди одной строкой («Ок, только семантика. Нужны регион и сужающие слова»). Блокер по
  стоп-полю — только когда пользователь хочет полную кампанию и не отвечает.
- **Не вываливай простыни.** Артефакт — в файл, сводка в чат 5-10 строк.
- **Гипотеза > пустой вопрос.** «Вот моя версия — согласны?»
- **Цифры сайта > догадки.** Есть счётчик — CPA, конверсии, гео и фразы бери из Метрики; нет — говори «истории нет», а не подставляй средние по рынку как факт.
- **Защищай качество на гейтах.** Если reference сказал «алярма — нет Метрики на сайте» — не закрывай это «ой ладно, как-нибудь без неё».
- **Бюджет важен** — предупреди при <30-50к ₽/мес.
- **Не выдумывай метрики и УТП.** «Не знаю» лучше выдуманного.
- **Пропуск всегда виден.** Шаг, выполненный без необязательной способности среды, получает пометку о пропуске в свой артефакт (формат — в «Что нужно от среды»), а на гейте ты говоришь об этом словами. Молча упрощённый результат — хуже честно недобранного.
- **References обязательны.** На шагах с пометкой «прочти `references/<name>.md` целиком» — это **не намёк**, а обязательное чтение.

# Возобновление

**Сначала — что сказал пользователь.** Возобновление идёт только по просьбе продолжить
(«продолжаем», «дальше», «давай следующий шаг»). Сообщение с другой просьбой — не повод
вернуться к плану: ответь на неё, а к `current_step` возвращайся, когда человек скажет.
Просьба из «Чего скилл не делает» — прямой отказ и стоп. «Продолжаем» после отказа —
продолжай с того места, где остановился, к теме отказа не возвращайся («ничего не удалял» и
т. п. не нужно).

«Продолжаем по Директу» → найди `direct-campaigns/*/`, прочитай `_state.json`, продолжай с того места. Не начинай сначала.

**Сначала прочитай `mode`.** `step:*` — оборвался посреди этапа (`current_step`) — доделай его.
Этап готов (`<шаг>.done`) — одной строкой скажи, что готово, и спроси, что дальше: другой этап
отдельно или полная кампания. Следующий шаг сам не предлагай и не начинай: строка «Дальше
можно» уже была в конце этапа. Выбрал полную — `mode: full`,
полный бриф без уже известных пунктов. Нет поля `mode` — это `full`.

В `full`: кабинет Директа (`direct_client_login`) и счётчик (`metrika_counter_id`) читай из `_state.json` и **не** спрашивай заново — Шаг 0.5 уже пройден. Оборвался посреди разведки — продолжай с первого незакрытого подпункта по `current_step` (`recon.direct` / `recon.metrika` / `recon.history`). Если кампания уже залита — этот скилл закончил работу; дальнейшие изменения — другая задача.

# При сбое шага

Проверь, что у шага есть все входы: каждый шаг читает артефакты предыдущих (их точный список — в «Передать» и «На выходе» самого шага). Нет входа — возвращайся на шаг, который его порождает, закрывай пробел, потом продолжай.