Yandex Performance Ops
Глобальный навык для работы с performance-маркетингом Яндекса из любого локального проекта.
Path Contract
<plugin-root> = корень этого bundle, где лежат .codex-plugin/plugin.json, skills/, mcp/, scripts/.
- Repo-local пример:
./plugins/yandex-direct-for-all
- Home-compatible install пример:
~/.codex/plugins/yandex-direct-for-all или ~/.claude/plugins/yandex-direct-for-all
<ops-skill-root> = <plugin-root>/skills/yandex-performance-ops
<client-lifecycle-root> = <plugin-root>/skills/yandex-direct-client-lifecycle
- В bundled docs и командах по умолчанию использовать
<plugin-root>/... или <ops-skill-root>/..., а не жёсткий ~/.codex/skills/...
Цель:
- хранить reusable-методологию, скрипты, промты и уроки в одном месте;
- держать в локальном проекте только контекст клиента и его уникальные правила;
- не терять наработки между
kartinium, siz, tenevoy и новыми проектами.
Что объединяет этот навык
Собрано и нормализовано из:
kartinium/.claude/skills/direct-search-semantics
kartinium/.claude/skills/yandex-direct
kartinium/.claude/skills/yandex-wordstat
kartinium/.claude/skills/roistat-direct
kartinium/.claude/skills/media-plan
ads/siz/.claude/skills/direct-optimization
ads/tenevoy/.claude/skills/direct-optimization
ads/siz/.claude/skills/yandex-metrika
ads/siz/.claude/skills/competitive-ads-extractor
- статистические подходы из
ads/siz/.claude/skills/ppc-data-analysis
Полная ревизия источников:
- source_inventory.md
- completeness_audit_2026-03-05.md
Этот global skill тоже не имеет права “перепридумывать” Wordstat/Direct слой мимо исходных source-skills.
Если есть конфликт между краткой интеграцией и source-skill по Wordstat, direct-search-semantics или yandex-direct, использовать source-skill как верхний канон.
Для Wordstat канонический reusable reference этого global skill лежит здесь:
- wordstat_collection_framework.md
- future_session_start_checklist.md
Global skill обязан уже сам содержать полный канон Wordstat-сбора.
Локальные project docs могут только добавлять client-specific терминологию, но не заменять global framework.
Для клиентского research-отчета Wordstat теперь считать обязательным три отдельных слоя:
- спрос по базовым маскам;
- сезонность;
- география.
Когда использовать
Используй этот навык, когда задача относится к одному из блоков:
- сбор новой поисковой семантики через официальный API;
- аудит и оптимизация уже работающих кампаний Яндекс.Директ;
- настройка и pre-moderation валидация поисковых кампаний;
- массовые проверки live-state через Direct API;
- SQR, минус-слова, структура, автотаргетинг, объявления, ExcludedSites;
- Roistat-first анализ лидов/продаж;
- Yandex Metrika отчёты и атрибуция;
- media-plan и plan-vs-fact;
- competitor creative research;
- синхронизация задач в YouGile;
- фиксация reusable-уроков после цикла работ.
Главный принцип
Global skill = методология + reusable tooling.
Local project = клиентский overlay + client-specific rules.
Обязательный client-facing report после live-apply
После любого live-изменения в кабинете нельзя ограничиваться коротким "сделано" или ссылкой на сырые JSON.
Нужно сразу собрать и показать пользователю human-readable report с тремя блоками:
что было — исходная проблема, counts, затронутые campaign_id/adgroup_id/ad_id;
что сделал — точные live-мутации, какие поля менялись, что сознательно не трогалось;
что стало — read-back и post-check с цифрами before/after.
Минимум, который обязан попасть в такой отчёт:
- список затронутых сущностей;
- количество реально изменённых объектов;
- пути к артефактам
apply_results / readback / summary;
- явное объяснение, почему были выбраны именно эти правки.
Если live-правки уже сделаны, но user-facing report ещё не показан, работа считается незавершённой.
Если user явно просит ускорить большой manual-review через локальные Codex CLI воркеры, это теперь канонический reusable path, а не разовая импровизация:
- launcher:
scripts/codex_cli_swarm_manual_review.py
- search prompt:
templates/codex_swarm_search_worker_prompt.md
- rsya prompt:
templates/codex_swarm_rsya_worker_prompt.md
- search schema:
schemas/codex_swarm_search_chunk_response.schema.json
- rsya schema:
schemas/codex_swarm_rsya_chunk_response.schema.json
Этот swarm-path разрешён только как ускорение ручного verdict-слоя:
- launcher обязан собирать единый full
worker knowledge pack из overlay + product/rules/lessons и отдавать его как canonical context для всего run;
- launcher обязан поверх full pack собирать
chunk focus context как deterministic extract по текущему chunk;
- каждый Codex worker получает как required reads только
chunk focus context + prior manual context + chunk TSV;
- full knowledge pack остаётся fallback-слоем и не должен быть обязательным read, если focus-context и prior-context уже достаточны;
- каждый worker обязан вручную покрыть каждый
candidate_id своего chunk;
- scripts могут только чанковать, запускать, валидировать coverage и merge'ить;
Для 15d creative/growth refresh теперь каноничен отдельный reusable path:
- если задача =
ротация text/image losers + новые группы / growth plan, не надо собирать полный giant-wave заново;
- собрать
Direct account snapshot на нужное окно через direct-orchestrator/scripts/collector_direct_account_snapshot.py c mode=ads, чтобы получить:
raw/direct_account_snapshot_v2/raw_bundle/ads/all_ads.tsv
raw/direct_account_snapshot_v2/raw_bundle/ad_texts/all_source_ads.json
raw/direct_account_snapshot_v2/raw_bundle/all_campaign_window_totals.tsv
- собрать search SQR на то же окно через
direct-orchestrator/scripts/collector_search_query_wave.py или fallback <ops-skill-root>/scripts/fetch_sqr.sh;
direct-orchestrator/scripts/local_wave_review.py теперь обязан терпеть волны без placements и без campaigns_meta.json: в creative-only / growth-only refresh он должен падать обратно на all_campaign_window_totals.tsv;
- reusable creative builder:
scripts/build_creative_rotation_from_outliers.py;
- reusable growth builder:
scripts/build_growth_structure_from_routes.py;
- creative builder обязан выпускать совместимый комплект:
03_creative_rotation_candidates.tsv
03_creative_rotation_candidates_v2.tsv
03_creative_rotation_skipped.tsv
03_creative_rotation_review.md
- growth builder обязан выпускать:
09_new_groups_candidates.tsv
09_missing_phrases_growth_review.md
12_growth_acceleration_pack.md
- а
09_structure_action_plan.tsv затем собирается штатным direct-orchestrator/scripts/build_structure_action_plan.py;
- verdict по новым standalone search-кампаниям нельзя высасывать из воздуха: если 15d growth-layer подтверждает только
new group / test layer, так и писать новая РК не подтверждена.
- Если пользователь дал
go на live apply после такого refresh, канонический reusable apply-path теперь такой:
- собрать text-rotation validation/apply pack через
scripts/build_text_rotation_apply_pack_from_tsv.py;
- прогнать
dry-run, затем live apply через scripts/apply_ad_replacement_pack.py;
- собрать manifest новых search adgroups и применить его через
scripts/apply_search_adgroup_manifest.py;
- сделать strict readback именно по новым
ad_ids и adgroup_ids, а не полагаться только на giant campaign_autotest.py по всей кампании;
- отправлять на модерацию только новые объявления через
send_to_moderation.py --ad-ids ...;
- RSYA image replacements не применять автоматически без visual/manual validation даже если refresh-docs их уже рекомендуют.
campaign_autotest.py может быть полезен как общий фон, но для creative/growth live-wave он не должен быть единственным safety-gate: старые WARN/FAIL по legacy-сущностям слишком шумные. Канонический gate = strict readback новых сущностей + targeted moderation readback.
Для strict pre-apply перед live-правками теперь каноничен отдельный local-pack path:
- Search strict local pack builder:
scripts/build_local_search_negatives_pack.py
- Search live drift-check:
scripts/dry_run_search_negatives_pack.py
- RSYA strict local pack builder:
scripts/build_local_rsya_excluded_sites_pack.py
- RSYA validator:
scripts/validate_excluded_sites_pack.py
- RSYA live drift-check/apply helper:
scripts/apply_no_moderation_pack.py
Правила этого preflight-контура:
- Search server-pack должен собираться только из
high-confidence stop-only слоя; generic single-word adjectives, numeric junk и low-confidence typo garbage должны отрезаться builder'ом до dry-run.
- RSYA local pack обязан строиться по live
ExcludedSites baseline, по placements evidence и по client formula (tail_formula_v3), а не тупо из manual decisions TSV.
- RSYA builder обязан быть
slot-aware: если в кампании мало свободных ExcludedSites, pack берёт только strongest candidates и паркует overflow в blocked audit.
- RSYA apply/readback обязан канонизировать идентификаторы площадок (
strip/lower, удаление схемы, www. и хвостового /) до drift-check и post-apply verify, потому что Direct может сам нормализовать домены/ids на readback.
- Validator и tail-formula не могут жить разными правилами: если client использует
tail_formula_v3, validate_excluded_sites_pack.py обязан валидировать именно по ней, а не по legacy >5 clicks & CTR>1%.
- scripts не имеют права придумывать verdict вместо worker-а.
- каждый chunk должен запускаться в изолированном
CODEX_HOME, чтобы parallel workers не дрались за system skills/install state.
- prompt обязан зажимать worker в короткий command-budget: сначала head knowledge-pack, потом prior-context и chunk, потом только targeted
rg/sed при реальном противоречии.
<cheap-codex-model> можно использовать как cheap default для bulk swarm only with guardrail: на Search он не должен быть единственным verdict-слоем. Mixed benchmark 2026-03-15 показал сильный bias к keep, особенно на brand-stop / route-fix / growth rows. Канонический режим: mini для draft/triage, сильная модель или человек для спорного хвоста и финального QA.
Для огромных Search SQR очередей канонический deterministic reduction-layer теперь такой:
- reusable script:
scripts/search_negative_marker_engine.py;
- project wrapper:
direct-orchestrator/scripts/run_search_negative_marker_cycle.py;
- это не auto-verdict и не auto-stop, а только shrink-engine перед manual negative review;
- обязательный порядок:
- bootstrap уже вручную подтверждённых
exclude / growth правил;
- apply этих правил к новой SQR очереди с audit-файлами
excluded и growth_hold;
- split остатка на
negative_candidate_rows и protected_route_hold;
- build compact marker cards только по
negative_candidate_rows;
- marker cards разрешены только двух типов:
token и phrase;
phrase имеет приоритет над token;
- growth/route-like хвост не должен смешиваться с negative-review и обязан уходить в
protected_route_hold или другой explicit hold-layer;
- user ничего не подтверждает вручную: все manual rules в этот слой приносит агент из уже просмотренных строк;
- карточка marker review обязана быть короткой:
marker, scope, matched_rows, cost/clicks, 3-5 примеров.
Минимальный reusable запуск:
python3 <ops-skill-root>/scripts/codex_cli_swarm_manual_review.py \
--kind search \
--queue <review/manual queue.tsv> \
--project-root <client project root> \
--merge-into <review/manual_decisions.tsv> \
--overlay <client overlay json> \
--local-skill <local skill path> \
--product-catalog <product catalog path> \
--search-rules <search rules path> \
--lessons <lessons path> \
--manual-decisions <existing manual decisions tsv> \
--workers 4 \
--chunk-size 25 \
--model <cheap-codex-model> \
--reasoning-effort medium \
--sandbox danger-full-access \
--approval-policy never
Секреты, board ids и брендовые аксиомы не зашивать в global-skill.
Правило 0: CLAUDE.md в каждом клиентском проекте (ОБЯЗАТЕЛЬНО!)
При инициализации ЛЮБОГО нового клиентского проекта Директа — ПЕРВЫМ делом создать .claude/CLAUDE.md в папке проекта со следующим содержимым:
# Проект: [Имя клиента]
## ЖЕЛЕЗНОЕ ПРАВИЛО
ПЕРЕД любым действием с кампаниями — СНАЧАЛА прочитай навык:
- `<ops-skill-root>/SKILL.md`
- optional project-local companion skill, если он реально существует
- `memory/lessons.md` в папке проекта (если есть)
НИКОГДА не импровизируй со ставками, стратегиями, минус-словами.
Все решения — ТОЛЬКО на основе навыка и данных.
## Валюта аккаунта: [KZT/RUB/BYN]
## OAuth токен: [путь к файлу]
## Логин: [client-login]
Без этого файла работа с проектом ЗАПРЕЩЕНА. Файл гарантирует что агент не начнёт импровизировать, а сначала прочитает навык и lessons.
Client overlay обязан переопределять build-layer до первого API write, если в нём заданы:
- стратегии
Search / РСЯ;
GoalId / PriorityGoals;
- гео (
RegionIds);
- правило по минус-словам для
РСЯ;
- формат
group names;
- формат
DisplayUrlPath.
- text guardrails и запрещённые термины для Direct copy.
Если overlay противоречит generic defaults скрипта, применять overlay, а не generic defaults.
Если overlay требует РСЯ без минус-фраз, clearing pattern в API = NegativeKeywords: null; пустой Items: [] не считать допустимым способом очистки.
Гео-правило на будущее:
МО в речи клиента неоднозначно и нельзя автоматически трактовать как область без Москвы;
- если клиент говорит
вся МО, полный МО, Москва и область, использовать полный регион RegionIds=[1];
- вариант
область без Москвы допустим только при явном подтверждении и тогда задаётся как RegionIds=[1,-213].
Client-specific rule sets для discovery и review должны лежать в локальных reference-файлах проекта и подключаться через overlay.
Минимум для reusable full-review:
references/product-catalog.md
references/search-stop-word-rules.json
references/rsya-placement-rules.json
Скрипты не должны знать конкретный GoalId, client_key, бренд клиента или special-case campaign id из кода. Эти значения надо брать из overlay, raw bundle или local references.
Перед любым новым циклом работ сначала проходить future_session_start_checklist.md.
Если в проекте уже есть готовые артефакты и рабочие скрипты, нельзя игнорировать их и идти через импровизацию.
Для RSYA stop-sites reusable-правило теперь такое:
Clicks >= 5 + (CTR > 1% или app/game/vpn) = только базовый каркас;
- финальный verdict обязан учитывать ещё
Cost, AvgCpc, goal conversions, campaign benchmark CPA, тип площадки и protected-platform hints;
- крупные платформы/marketplace нельзя блокировать по одному package-id;
- если есть
clicks > 0 и cost = 0, агент не перекладывает это на пользователя, а сам ставит действие перепроверка raw/source -> потом стоп или мониторинг следующей волны.
Для больших RSYA placement очередей теперь каноничен отдельный deterministic queue prefilter ДО ручного verdict-слоя:
- reusable script:
scripts/prefilter_rsya_manual_queue.py;
- это не auto-stop и не auto-verdict, а только фильтр очереди
manual review / monitor / anomaly quarantine;
- safe-default для всех клиентов:
low_signal_skip: conversions = 0, clicks < 3, CTR < 1%, cost < 20;
zero_click_tail_skip: clicks = 0, cost = 0, impressions < 150;
protected_low_signal_skip: protected/yandex, conversions = 0, clicks < 5, cost < 100;
app_like_low_signal_skip: app-like, conversions = 0, clicks < 2, cost < 35;
anomaly_quarantine: clicks > 0 and cost = 0 или conversions > 0 and cost = 0.
- пороги prefilter хранить в client/local rules file (
queue_prefilter), а не в коде;
- строки из
auto_skipped нельзя считать готовыми stop-sites: это только monitor/skip from manual stop review;
- строки из
anomaly_quarantine не должны попадать ни в auto-stop, ни в обычный manual-stop shortlist до raw/source recheck.
Если build, структура или тексты начинаются после upstream-исследования через lifecycle-слой, downstream skill обязан сначала проверить наличие трех ручных артефактов:
research/analysis/единая-карта-конкурентов.md
research/analysis/пакет-структуры-будущего-кабинета.md
research/analysis/пакет-текстов-и-офферов.md
research/analysis/готовые-тексты-для-директа.tsv
Если их нет, не перескакивать сразу к сборке кабинета "из головы", а сначала дособрать этот upstream handoff.
Перед сборкой текстов или отправкой их человеку предпочтителен такой путь:
- ручная подготовка текстов в
готовые-тексты-для-директа.tsv;
- прогон через
scripts/validate_direct_copy_pack.py;
- только потом перенос в build-слой.
Если задача дошла до стадии pre-moderation, reusable-канон теперь требует отдельного channel split:
Search и РСЯ считаются разными deliverables и не собираются из одного универсального copy-pack.
- До build/moderation handoff должны существовать отдельные артефакты:
search-group-map
search-negative-bundles
search-ad-copy-pack
rsya-group-map
rsya-copy-and-image-pack
- Для
Search обязательны:
- точная группа/интент;
- landing;
- negative bundle;
- text guardrails.
- Для
РСЯ обязательны:
- audience;
- trigger;
- message angle;
- image brief;
- avoid-list для модерации и mismatch intent.
5 уникальных ad variants на каждую группу;
5 уникальных изображений на каждую группу;
- file-first карта
group x variant -> template_id/title/image_url, если изображения берутся из продуктовых шаблонов.
- Нельзя переносить поисковый copy-pack в
РСЯ без отдельной переработки под visual/message intent.
- Нельзя считать
модерация готова, пока этот channel split не собран и не проверен.
- Для
Search production default:
- на каждую группу должно быть
ровно 5 уникальных объявлений до стадии moderation-ready;
- все
5 должны соответствовать ключам внутри этой группы и заметно отличаться друг от друга по angle / promise / pain / CTA.
- default bidding strategy =
WB_MAXIMUM_CONVERSION_RATE с оплатой за клики;
- default
GoalId для стратегии = 13, если overlay явно не задаёт другой search goal;
- для
Search в auto strategy нельзя оставлять BidCeiling=null; нужен явный BidCeiling / max CPC из overlay или approved bid baseline;
- если в кампании до этого стоял manual bidding и есть
DailyBudget, при переводе в auto strategy budget нужно переносить в weekly layer, а DailyBudget сбрасывать в null.
- Для
РСЯ production default:
- на каждую группу должно быть
ровно 5 уникальных объявлений;
- на каждую такую пятёрку должно быть
5 уникальных изображений;
- default bidding strategy =
PAY_FOR_CONVERSION_MULTIPLE_GOALS;
- default optimisation intent для
РСЯ = все approved lead goals клиента: звонок / форма / messenger, если пользователь не задал иной shortlist;
- для unified
РСЯ с несколькими целями использовать exact enum PAY_FOR_CONVERSION_MULTIPLE_GOALS и nested block PayForConversionMultipleGoals;
PriorityGoals должны содержать все approved lead goals клиента; без них multi-goal стратегия невалидна;
- single-goal
PAY_FOR_CONVERSION допустим только как явно согласованный fallback; в нём обязателен GoalId и Cpa;
WB_MAXIMUM_CLICKS в РСЯ нельзя ставить по умолчанию.
- если пользователь просит брать изображения из шаблонов, default source =
последние реальные template covers из live-каталога/БД, а не выдуманные concept-art placeholders.
- если изображения берутся с сайта клиента, брать их только с соответствующей landing/page этого кластера; переносить фото между чужими посадочными нельзя.
DisplayUrlPath не должен быть техническим id/slugs вида p1-*, если у клиента нет такого явного правила. Default = человекочитаемый путь.
DisplayUrlPath должен проходить build-time валидацию: человекочитаемый, без технических префиксов и длиной не более 20 символов.
- имена групп в кабинете не должны оставаться техническими кодами, если не требуется служебная отладка. Default = человекопонятные имена.
- если клиент явно требует
РСЯ без минус-фраз, build-layer не должен автоматически протаскивать generic negative bundles в РСЯ.
- если клиент задаёт разные правила оптимизации для
Search и РСЯ, global skill обязан сохранить channel-specific split и не пытаться выровнять стратегии между каналами.
- в текстах Direct слово
WhatsApp запрещено: не использовать его в Title, Title2, Text, callouts, sitelink titles/descriptions.
- если на сайте есть WhatsApp как факт, в Direct copy использовать нейтральные замены типа
быстрая связь, связь с менеджером, быстрый расчет.
- статус
live / включено нельзя объявлять по одному ResumeResults. После campaigns.resume обязателен свежий campaigns.get с проверкой State, Status, StatusPayment, StatusClarification.
- если после
resume кампания остаётся State=OFF, skill обязан назвать точный blocker из live API и не писать, что запуск завершён.
Товарный или фидовый слой не считать обязательной частью стандартного пакета.
Добавлять его только по прямому запросу пользователя или после отдельного решения в клиентском документе.
Для competitor-review reusable-слой обязан хранить не только лидирующие домены, но и сами тексты объявлений конкурентов:
- отдельный generated file с колонками
query / region / domain / title / snippet / url;
- HTML-отчёт обязан показывать этот слой напрямую, а не только счётчики появлений.
Для клиентского веб-отчета канонический путь теперь такой:
- подготовленные ручные артефакты;
- машинные рендеры таблиц;
- HTML-страница без ссылок на внутренние markdown-файлы;
build_secure_client_report.py;
- локальная проверка;
- mobile check
390px;
- live check после деплоя.
Client Overlay Contract
По умолчанию навык ищет локальный файл клиента в таком порядке:
./.codex/yandex-performance-client.json
./claude/yandex-performance-client.json
./.claude/yandex-performance-client.json
- путь из
YANDEX_PERFORMANCE_CLIENT_CONTEXT
В public bundle client overlays не хранятся. Они должны жить в локальном private project-layer вне git, например:
./.codex/yandex-performance-client.json
Шаблоны:
- client_context.example.json
- routing_map.example.tsv
- campaign_id_map.example.json
- copy_map.example.json
Описание полей:
- local_overlay_contract.md
- yandex_cloud_search_handoffs.md
Быстрый scaffold:
python3 <ops-skill-root>/scripts/init_client_context.py \
--output ./.codex/yandex-performance-client.json \
--client-key acme
Источники данных и иерархия истины
- Direct API / Reports API / Wordstat API / Roistat API / Metrika API
- Локальные raw-файлы, собранные скриптами
- Локальный client overlay
- Локальные client-specific skills/docs
- Markdown-документация и агентские выводы
Если live-data конфликтует с локальными доками, верить live-data.
Если пользователь сказал что локальные raw-выгрузки устарели, пункт 2 временно исключается из принятия решений до нового live-сбора.
Жёсткие правила
ПАРСИНГ != АНАЛИЗ
- парсинг только официальными API и скриптами;
- анализ делать только вручную мной по raw-файлам;
- не домешивать новые API-вызовы в фазу анализа.
- если в локальном проекте есть
build_decision_report.py, build_executive_review.py, build_executive_html.py или похожие рендеры, они не имеют права повышать review/generated/*safe_ready.tsv до пользовательского статуса ready_now. User-facing verdict слой обязан идти только из review/manual/*.tsv и, если проект хранит решения отдельными файлами, review/manual_decisions/*.tsv.
- если ручной verdict по строке не внесён, верхний слой обязан прямо показывать
manual gate incomplete / manual_verdict_required, а не маскировать machine shortlist под готовый пакет правок.
- reusable queue-builders обязаны поддерживать и legacy raw paths, и новые
*_v2/raw_bundle/* пути; path drift не оправдывает переход назад к machine-only verdict.
- scripts имеют право только:
- собирать;
- чистить;
- нормализовать;
- сортировать;
- чанковать;
- рендерить данные.
- scripts не имеют права:
- ставить вердикты;
- предлагать стоп-слова, стоп-площадки, рост, ставки, новые группы, мониторинг;
- решать что target / non-target вместо ручного построчного анализа.
- analysis-скрипты для классификации ключей, фраз, минус-слов и масок запрещены.
- анализ ключевых слов из Wordstat, SQR и Roistat делать без скриптов, только вручную по raw-выгрузкам.
- upstream research по SERP/footprint/competitor pages тоже не собирать вручную по одной фразе.
Default path = batch job-spec + collector script.
Для этого использовать:
<client-lifecycle-root>/scripts/yandex_search_batch.py
<client-lifecycle-root>/scripts/yandex_search_ads_batch.py
<client-lifecycle-root>/scripts/build_domain_shortlist_from_serp.py
<client-lifecycle-root>/scripts/firecrawl_scrape.py --jobs-file ...
<client-lifecycle-root>/scripts/build_followup_jobs_from_serp.py
<client-lifecycle-root>/scripts/split_tsv_batch.py
<client-lifecycle-root>/scripts/merge_sitemap_batch_outputs.py
<client-lifecycle-root>/scripts/render_serp_wave.py
<client-lifecycle-root>/scripts/render_ad_serp_wave.py
<client-lifecycle-root>/scripts/render_sitemap_candidates.py
<client-lifecycle-root>/scripts/render_page_capture_inventory.py
- полный competitor collection строить не от случайных стартовых запросов, а от вручную валидированного keyword set.
Допустим ранний scout/reconnaissance для проверки рынка и пайплайна, но exhaustive
organic SERP / ad SERP waves запускаются только после этапа:
- official
Wordstat raw;
- ручная валидация масок, ключей и минус-логики;
- формирование job-matrix
keyword x geo.
- для каждого validated keyword нужно сохранять raw-query trail и потом расширять найденные домены через sitemap/page-capture.
- до follow-up сборов сначала строить таблицу повторяемости доменов и вручную утверждать укороченный shortlist.
Default path на будущее:
- брать
топ-15 повторяющихся доменов из подтвержденной выдачи Яндекса;
- до shortlist-builder-а механически исключать очевидные некоммерческие URL-паттерны: статьи, новости, справочники, PDF;
- не тянуть
sitemap/page-capture по длинному хвосту слабых доменов.
- после live
organic SERP wave follow-up jobs должны тоже строиться скриптом, а не вручную:
serp_results.tsv -> page-capture-jobs.tsv
serp_results.tsv -> sitemap-jobs.tsv
- затем только batch collectors по этим job-файлам.
- если batch слишком большой или медленный, разбивать jobs нужно тоже скриптом:
split_tsv_batch.py для chunk-files;
- затем несколько collector workers по chunk-TSV;
- затем merge/normalize step скриптом, без ручной склейки.
- Wordstat только официальный
- запрещён веб-скрейп Wordstat;
numPhrases=2000 обязателен для полного охвата масок.
- канонический порядок всегда такой:
СТРУКТУРА -> МАСКИ -> РЕВЬЮ МАСОК -> ПАРСИНГ СКРИПТОМ -> [полный успех] -> АНАЛИЗ -> ЧИСТКА -> ГРУППИРОВКА;
- нельзя перепрыгивать из масок сразу в анализ;
- нельзя делать one-off
wordstat_* вызовы вместо wave-collector workflow.
- до любого парсинга обязателен
product map:
- официальные названия;
- разговорные названия;
- тендерные/закупочные формулировки;
- жаргон;
- аббревиатуры;
- ошибки написания;
- латиница/кириллица;
- применения по отраслям.
Wave 1 обязан начинаться с L1 root-масок.
Где это семантически возможно, root-маски должны быть однословными.
- после
L1 в тот же Wave 1 добавляются L2 product masks.
Для нового круга правил:
Wave 1 в 9/10 случаев строится на однословных масках;
Wave 2 допускает двухсловные маски;
- трехсловные маски не считать default path без явной причины.
- в клиентском или внутреннем отчете слой спроса из Wordstat нужно показывать отдельно:
- широкие корневые маски как обзорный ландшафт;
- точные базовые маски как рабочий слой;
- использовать только
totalCount по маске;
- не суммировать вложенные запросы и не складывать маски между собой как единый объем рынка.
- но для ручного анализа
totalCount недостаточен:
- по каждой approved mask надо собирать полный официальный ceiling
2000 строк = 40 страниц;
- затем лично просматривать каждую строку
topRequests и associations;
- только после такого row-by-row review разрешено выделять
target, new mask, adjacent, stop-candidate, noise.
- для SQR manual-review разрешены только два вида deterministic propagation из manual-approved слоя:
- если стоп-слово уже подтверждено вручную в прошлой или текущей волне, можно автоматически убрать из новой очереди unresolved-строки, где это exact single-word минус встречается в
query/criterion;
- это не новый verdict, а dedupe/preprocessing;
- если уже внесён ручной verdict c exact query / exact token / exact phrase внутри конкретного
ad_group_name, можно строить manual-approved rulebook и детерминированно распространять это решение на unresolved-строки только при exact/scope-safe match;
- такой propagation не придумывает новый action: он берёт только уже утверждённый
assistant_action/assistant_reason из manual decision;
- конфликтующие matches не auto-apply, а уходят в отдельный conflict-файл;
- любой skip/propagation обязан идти с audit-файлами
что исключено, rulebook, auto-decisions, conflicts, remaining.
- если full row-by-row manual-review Search-хвоста становится неэкономным, перед любым swarm/manual escalation разрешён только один дополнительный deterministic слой:
search_negative_marker_engine.py;
- он не имеет права принимать verdict за строку;
- он имеет право только:
- bootstrap already-approved
exclude и park_growth rules;
- автоматически вычитать строки, уже покрытые этими правилами;
- автоматически парковать
growth/route/protected хвост вне negative-review;
- строить компактные marker cards по оставшемуся
negative_candidate слою.
- канонические выходы этого слоя:
search_excluded_by_marker_rules.tsv
search_growth_hold.tsv
search_protected_route_hold.tsv
search_negative_candidate_rows.tsv
search_negative_marker_cards.tsv
search_negative_marker_examples.tsv
- good-state для этого слоя:
- active negative review становится на порядок меньше raw queue;
- целевые хвосты типа
потолки/LED/скрытый монтаж/парящий профиль/плинтус не попадают в marker cards только потому, что там встретился случайный модификатор;
- явный non-target хвост (
другой товар, B2B, чужой бренд, marketplace, alien use-case) остаётся в negative candidates.
Для Search negatives перед live apply теперь обязателен отдельный dry-run слой, а не только validation JSON:
- reusable script:
scripts/dry_run_search_negatives_pack.py;
- он читает уже подготовленный
search_negatives_pack_apply.json, снимает live NegativeKeywords по adgroup и проверяет:
- drift между live baseline и
before_keywords из pack;
- сколько минусов уже стоят в группе;
- сколько реально будет добавлено после merge;
- если есть drift, status должен быть
blocked, а live apply запрещён до пересборки pack;
- canonical outputs: JSON + text report с
drift_count, dry_run_add_count, skip_existing_count.
Для RSYA apply теперь канонический hard blocker такой:
- если validation/analysis слой всё ещё содержит
manual_review_count > 0, validation_pack_rsya.py обязан ставить status=blocked;
prepare_apply_rsya_excluded_sites.py обязан повторно hard-fail'ить, если в validation summary не ноль manual tail;
- правило простое:
RSYA not touch until manual tail is closed.
- если пользователь просит
просмотреть поисковые фразы, собрать минус-фразы, разобрать SQR, посмотреть новые поисковые фразы или явно требует вручную каждую строку, default path только такой:
- свежий live/raw сбор;
- полный ручной row-by-row review каждой строки;
- сохранение verdict-слоя в
review/manual/*.tsv или эквивалентный manual-layer;
- сбор decision table/report;
- reduction-layer: phrase-level evidence из manual-layer обязано быть сведено к production-safe stop-words / коротким safe-маскам на нужном scope;
- отдельный validation-layer:
single-token only или явно одобренные короткие safe-маски, плюс conflict-check с target words;
- и только потом pre-apply pack из
approved_negative.
- если объём manual-review слишком велик и user явно разрешил swarm:
- резать очередь на bounded chunks;
- запускать несколько локальных
codex exec воркеров на <cheap-codex-model> с model_reasoning_effort="medium" по умолчанию;
- для escalation / conflict-validation / final QA поднимать более сильную модель (
<strong-codex-model> или project-approved codex model) только на спорный хвост;
- для local file-only review по умолчанию НЕ копировать пользовательский
config.toml в worker CODEX_HOME, чтобы воркеры не поднимали лишние MCP-серверы и не тратили токены на startup-шум;
- промт воркера обязан ссылаться на global skill, local skill, overlay, product catalog / local rules, existing manual decisions и chunk TSV;
- worker не имеет права редактировать master queue / master decisions напрямую, только вернуть schema-valid JSON;
- launcher обязан провалить chunk, если
candidate_id coverage неполный, есть extra ids, есть duplicates или пустые assistant_action / assistant_reason;
- merge в
manual_decisions.tsv разрешён только после такого validation pass.
- запрещено начинать такой workflow с:
- machine shortlist;
safe_ready;
- auto-mined stop words;
- broad phrase collapse;
- удаления уже добавленных или новых поисковых фраз до ручного verdict по строке.
- если в кабинете уже есть добавленные фразы, новые поисковые фразы или ранее залитые минуса, это не повод механически их убирать.
Сначала вручную смотреть сырые строки поисковых фраз, потом принимать решение по exact query/token/phrase.
- live apply/rollback по SQR-минусам заблокирован, пока manual gate не закрыт полностью.
- дубликаты можно схлопывать только после ручного verdict по exact query.
Нельзя сначала схлопнуть хвост, а потом делать вид, что вся группа строк уже просмотрена вручную.
- phrase-level evidence не равно production-ready минус-фраза.
Фразы из manual SQR review нельзя лить в кабинет как есть, если из них можно безопасно выделить короткий блокирующий токен или короткую safe-маску.
- канонический production-layer для SQR-negatives:
review/manual/* = evidence и verdict;
review/manual_reduced/* или эквивалент = сокращённые stop-words / safe-маски;
live_apply/*negative_tasks*.tsv разрешён только из reduced-layer.
- reusable apply-path по умолчанию обязан отклонять tasks, где negative params содержат
phrase, если нет отдельного explicit override от пользователя и письменного объяснения, почему token-reduction невозможен.
- user-facing/client-facing отчет обязан различать:
по каким поисковым фразам нашли проблему;
какие короткие стоп-слова или safe-маски реально добавили.
Нельзя выдавать phrase-level evidence за список реально добавленных production-stop-слов.
- канонический renderer для этого слоя:
scripts/render_wordstat_mask_demand.py
- вход = config TSV с approved masks и путями к raw;
- выход =
wordstat-demand-exact.tsv, wordstat-demand-roots.tsv, _summary.json
- обязательные соседние renderer-слои:
scripts/render_wordstat_seasonality.py
scripts/render_wordstat_geo.py
- выход =
wordstat-seasonality-matrix.tsv, wordstat-geo-priority.tsv, _summary.json
- канонический collector обязан уметь собирать и эти raw-слои:
--dynamics true
--regions-report true
--regions-tree true
- если для сезонности или географии возникает соблазн сделать разовый
wordstat_* вызов вручную, это считать нарушением workflow.
Сначала расширять или переиспользовать scripts/wordstat_collect_wave.js.
- после составления
Wave 1 обязателен отдельный mask review:
- web/source synonym review;
- Wordstat association review на широких масках;
- только потом запуск collector-а.
Pre-moderation = отдельный gate, а не хвост build-этапа
- до
ads.moderate должны быть собраны и проверены:
- отдельный
Search pack;
- отдельный
РСЯ pack;
- channel-specific negatives / intent guards;
- moderation-safe promises;
- image brief / actual creatives для
РСЯ;
- live-readiness checks.
- если чего-то из этого нет, статус должен оставаться
handoff-ready, но не moderation-ready.
Wave 1 и Wave 2 обязательны.
Wave 2 строится из gap-analysis по итогам Wave 1, а не угадыванием “что еще спросить”.
- парсинг Wordstat допустим только reusable collector-ом из
masks-file -> raw files.
Парсинг вручную по одной маске через MCP/tool вызовы запрещён.
- канонический Wordstat entrypoint на этом маке:
bash <ops-skill-root>/scripts/wordstat_tool.sh preflight ...
bash <ops-skill-root>/scripts/wordstat_tool.sh collect-wave ...
bash <ops-skill-root>/scripts/wordstat_tool.sh preflight-save ...
bash <ops-skill-root>/scripts/wordstat_tool.sh collect-wave-save ...
- discovery order для Wordstat всегда такой:
- global wrapper
<ops-skill-root>/scripts/wordstat_tool.sh;
- global
wordstat_preflight.sh / wordstat_collect_wave.js;
- только потом project-local fallback из
.claude/skills/direct-search-semantics/scripts/.
- локальные project scripts нельзя молча считать primary path, если global canonical wrapper доступен.
- режим по умолчанию для агентской работы =
file-first:
- raw, summaries, logs и render outputs сначала сохранять в файлы;
- в контекст не вытаскивать сырые rows/JSON, если это не нужно для точечной проверки;
- после сбора открывать уже сохранённые
.tsv/.json/.md частями через sed/head/rg.
- до анализа обязателен completeness gate:
- число raw-файлов должно совпадать с числом масок;
- пустые/ошибочные raw-файлы должны быть выявлены;
- новые маски из associations должны быть вынесены в gap/wave2 backlog.
- analysis-скрипты для классификации Wordstat-ключей и минус-слов запрещены.
После полного raw collection анализ делать только вручную агентами/оператором по raw bundle.
- Не считать Wordstat автоматически только
OAuth-задачей или только Cloud-задачей.
- Сначала нужно live-проверкой определить, какой официальный путь реально доступен клиенту:
- существующий legacy OAuth-app path;
- или
Yandex Cloud Search API -> Wordstat.
- Если legacy path исторически работал у клиента, его нельзя отбрасывать без проверки.
- Если
oauth токен содержит wordstat:api, но live collector получает 403 Forbidden, это не считать просто "нужно заново авторизоваться".
Нужно проверить:
- корректный method/header;
- не упирается ли проект в
ClientId/app approval;
- не нужен ли переход на cloud-path.
- Если preflight написан на
httpx, не использовать response.ok: у httpx authoritative-флаг успеха это response.is_success.
- Operator-facing Wordstat status должен различать:
- внутренний баг интеграции;
blocked по 401/403;
ready.
Нельзя показывать человеку общее failed, если live diagnostics уже доказывают конкретный blocked verdict по endpoint checks.
- Для cloud-варианта заранее фиксировать:
folder_id
- auth mode (
API key или IAM token/service account)
- роль на сервис-аккаунте
search-api.webSearch.user
- какой именно Search API endpoint используется в collect
…(truncated)
1---2name: yandex-performance-ops3description: Глобальный навык для Яндекс.Директ/Wordstat/Roistat/Метрики: сбор новой семантики, аудит и оптимизация действующих кампаний, live-валидация, media-plan, competitor research и client-overlay контракт для любого локального проекта.4---56# Yandex Performance Ops78Глобальный навык для работы с performance-маркетингом Яндекса из любого локального проекта.910## Path Contract1112- `<plugin-root>` = корень этого bundle, где лежат `.codex-plugin/plugin.json`, `skills/`, `mcp/`, `scripts/`.13- Repo-local пример: `./plugins/yandex-direct-for-all`14- Home-compatible install пример: `~/.codex/plugins/yandex-direct-for-all` или `~/.claude/plugins/yandex-direct-for-all`15- `<ops-skill-root>` = `<plugin-root>/skills/yandex-performance-ops`16- `<client-lifecycle-root>` = `<plugin-root>/skills/yandex-direct-client-lifecycle`17- В bundled docs и командах по умолчанию использовать `<plugin-root>/...` или `<ops-skill-root>/...`, а не жёсткий `~/.codex/skills/...`1819Цель:20- хранить reusable-методологию, скрипты, промты и уроки в одном месте;21- держать в локальном проекте только контекст клиента и его уникальные правила;22- не терять наработки между `kartinium`, `siz`, `tenevoy` и новыми проектами.2324## Что объединяет этот навык2526Собрано и нормализовано из:27- `kartinium/.claude/skills/direct-search-semantics`28- `kartinium/.claude/skills/yandex-direct`29- `kartinium/.claude/skills/yandex-wordstat`30- `kartinium/.claude/skills/roistat-direct`31- `kartinium/.claude/skills/media-plan`32- `ads/siz/.claude/skills/direct-optimization`33- `ads/tenevoy/.claude/skills/direct-optimization`34- `ads/siz/.claude/skills/yandex-metrika`35- `ads/siz/.claude/skills/competitive-ads-extractor`36- статистические подходы из `ads/siz/.claude/skills/ppc-data-analysis`3738Полная ревизия источников:39- [source_inventory.md](references/source_inventory.md)40- [completeness_audit_2026-03-05.md](references/completeness_audit_2026-03-05.md)4142Этот global skill тоже не имеет права “перепридумывать” `Wordstat/Direct` слой мимо исходных source-skills.43Если есть конфликт между краткой интеграцией и source-skill по `Wordstat`, `direct-search-semantics` или `yandex-direct`, использовать source-skill как верхний канон.4445Для `Wordstat` канонический reusable reference этого global skill лежит здесь:46- [wordstat_collection_framework.md](references/wordstat_collection_framework.md)47- [future_session_start_checklist.md](references/future_session_start_checklist.md)48Global skill обязан уже сам содержать полный канон Wordstat-сбора.49Локальные project docs могут только добавлять client-specific терминологию, но не заменять global framework.50Для клиентского research-отчета Wordstat теперь считать обязательным три отдельных слоя:51- спрос по базовым маскам;52- сезонность;53- география.5455## Когда использовать5657Используй этот навык, когда задача относится к одному из блоков:58- сбор новой поисковой семантики через официальный API;59- аудит и оптимизация уже работающих кампаний Яндекс.Директ;60- настройка и pre-moderation валидация поисковых кампаний;61- массовые проверки live-state через Direct API;62- SQR, минус-слова, структура, автотаргетинг, объявления, ExcludedSites;63- Roistat-first анализ лидов/продаж;64- Yandex Metrika отчёты и атрибуция;65- media-plan и plan-vs-fact;66- competitor creative research;67- синхронизация задач в YouGile;68- фиксация reusable-уроков после цикла работ.6970## Главный принцип7172Global skill = методология + reusable tooling.7374Local project = клиентский overlay + client-specific rules.7576## Обязательный client-facing report после live-apply7778После любого live-изменения в кабинете нельзя ограничиваться коротким "сделано" или ссылкой на сырые JSON.79Нужно сразу собрать и показать пользователю human-readable report с тремя блоками:80- `что было` — исходная проблема, counts, затронутые `campaign_id/adgroup_id/ad_id`;81- `что сделал` — точные live-мутации, какие поля менялись, что сознательно не трогалось;82- `что стало` — read-back и post-check с цифрами `before/after`.8384Минимум, который обязан попасть в такой отчёт:85- список затронутых сущностей;86- количество реально изменённых объектов;87- пути к артефактам `apply_results / readback / summary`;88- явное объяснение, почему были выбраны именно эти правки.8990Если live-правки уже сделаны, но user-facing report ещё не показан, работа считается незавершённой.9192Если user явно просит ускорить большой manual-review через локальные Codex CLI воркеры, это теперь канонический reusable path, а не разовая импровизация:93- launcher: `scripts/codex_cli_swarm_manual_review.py`94- search prompt: `templates/codex_swarm_search_worker_prompt.md`95- rsya prompt: `templates/codex_swarm_rsya_worker_prompt.md`96- search schema: `schemas/codex_swarm_search_chunk_response.schema.json`97- rsya schema: `schemas/codex_swarm_rsya_chunk_response.schema.json`9899Этот swarm-path разрешён только как ускорение ручного verdict-слоя:100- launcher обязан собирать единый full `worker knowledge pack` из overlay + product/rules/lessons и отдавать его как canonical context для всего run;101- launcher обязан поверх full pack собирать `chunk focus context` как deterministic extract по текущему chunk;102- каждый Codex worker получает как required reads только `chunk focus context + prior manual context + chunk TSV`;103- full knowledge pack остаётся fallback-слоем и не должен быть обязательным read, если focus-context и prior-context уже достаточны;104- каждый worker обязан вручную покрыть каждый `candidate_id` своего chunk;105- scripts могут только чанковать, запускать, валидировать coverage и merge'ить;106107Для 15d creative/growth refresh теперь каноничен отдельный reusable path:108- если задача = `ротация text/image losers` + `новые группы / growth plan`, не надо собирать полный giant-wave заново;109- собрать `Direct account snapshot` на нужное окно через `direct-orchestrator/scripts/collector_direct_account_snapshot.py` c `mode=ads`, чтобы получить:110 - `raw/direct_account_snapshot_v2/raw_bundle/ads/all_ads.tsv`111 - `raw/direct_account_snapshot_v2/raw_bundle/ad_texts/all_source_ads.json`112 - `raw/direct_account_snapshot_v2/raw_bundle/all_campaign_window_totals.tsv`113- собрать search SQR на то же окно через `direct-orchestrator/scripts/collector_search_query_wave.py` или fallback `<ops-skill-root>/scripts/fetch_sqr.sh`;114- `direct-orchestrator/scripts/local_wave_review.py` теперь обязан терпеть волны без `placements` и без `campaigns_meta.json`: в creative-only / growth-only refresh он должен падать обратно на `all_campaign_window_totals.tsv`;115- reusable creative builder: `scripts/build_creative_rotation_from_outliers.py`;116- reusable growth builder: `scripts/build_growth_structure_from_routes.py`;117- creative builder обязан выпускать совместимый комплект:118 - `03_creative_rotation_candidates.tsv`119 - `03_creative_rotation_candidates_v2.tsv`120 - `03_creative_rotation_skipped.tsv`121 - `03_creative_rotation_review.md`122- growth builder обязан выпускать:123 - `09_new_groups_candidates.tsv`124 - `09_missing_phrases_growth_review.md`125 - `12_growth_acceleration_pack.md`126 - а `09_structure_action_plan.tsv` затем собирается штатным `direct-orchestrator/scripts/build_structure_action_plan.py`;127- verdict по новым standalone search-кампаниям нельзя высасывать из воздуха: если 15d growth-layer подтверждает только `new group` / `test layer`, так и писать `новая РК не подтверждена`.128- Если пользователь дал `go` на live apply после такого refresh, канонический reusable apply-path теперь такой:129 1. собрать text-rotation validation/apply pack через `scripts/build_text_rotation_apply_pack_from_tsv.py`;130 2. прогнать `dry-run`, затем live apply через `scripts/apply_ad_replacement_pack.py`;131 3. собрать manifest новых search adgroups и применить его через `scripts/apply_search_adgroup_manifest.py`;132 4. сделать strict readback именно по новым `ad_ids` и `adgroup_ids`, а не полагаться только на giant `campaign_autotest.py` по всей кампании;133 5. отправлять на модерацию только новые объявления через `send_to_moderation.py --ad-ids ...`;134 6. RSYA image replacements не применять автоматически без visual/manual validation даже если refresh-docs их уже рекомендуют.135- `campaign_autotest.py` может быть полезен как общий фон, но для creative/growth live-wave он не должен быть единственным safety-gate: старые `WARN/FAIL` по legacy-сущностям слишком шумные. Канонический gate = strict readback новых сущностей + targeted moderation readback.136137Для strict pre-apply перед live-правками теперь каноничен отдельный local-pack path:138- Search strict local pack builder: `scripts/build_local_search_negatives_pack.py`139- Search live drift-check: `scripts/dry_run_search_negatives_pack.py`140- RSYA strict local pack builder: `scripts/build_local_rsya_excluded_sites_pack.py`141- RSYA validator: `scripts/validate_excluded_sites_pack.py`142- RSYA live drift-check/apply helper: `scripts/apply_no_moderation_pack.py`143144Правила этого preflight-контура:145- Search server-pack должен собираться только из `high-confidence stop-only` слоя; generic single-word adjectives, numeric junk и low-confidence typo garbage должны отрезаться builder'ом до dry-run.146- RSYA local pack обязан строиться по live `ExcludedSites` baseline, по placements evidence и по client formula (`tail_formula_v3`), а не тупо из manual decisions TSV.147- RSYA builder обязан быть `slot-aware`: если в кампании мало свободных `ExcludedSites`, pack берёт только strongest candidates и паркует overflow в blocked audit.148- RSYA apply/readback обязан канонизировать идентификаторы площадок (`strip/lower`, удаление схемы, `www.` и хвостового `/`) до drift-check и post-apply verify, потому что Direct может сам нормализовать домены/ids на readback.149- Validator и tail-formula не могут жить разными правилами: если client использует `tail_formula_v3`, `validate_excluded_sites_pack.py` обязан валидировать именно по ней, а не по legacy `>5 clicks & CTR>1%`.150- scripts не имеют права придумывать verdict вместо worker-а.151- каждый chunk должен запускаться в изолированном `CODEX_HOME`, чтобы parallel workers не дрались за system skills/install state.152- prompt обязан зажимать worker в короткий command-budget: сначала head knowledge-pack, потом prior-context и chunk, потом только targeted `rg/sed` при реальном противоречии.153- `<cheap-codex-model>` можно использовать как cheap default для bulk swarm only with guardrail: на Search он не должен быть единственным verdict-слоем. Mixed benchmark `2026-03-15` показал сильный bias к `keep`, особенно на brand-stop / route-fix / growth rows. Канонический режим: `mini` для draft/triage, сильная модель или человек для спорного хвоста и финального QA.154155Для огромных Search SQR очередей канонический deterministic reduction-layer теперь такой:156- reusable script: `scripts/search_negative_marker_engine.py`;157- project wrapper: `direct-orchestrator/scripts/run_search_negative_marker_cycle.py`;158- это не auto-verdict и не auto-stop, а только shrink-engine перед manual negative review;159- обязательный порядок:160 - bootstrap уже вручную подтверждённых `exclude` / `growth` правил;161 - apply этих правил к новой SQR очереди с audit-файлами `excluded` и `growth_hold`;162 - split остатка на `negative_candidate_rows` и `protected_route_hold`;163 - build compact marker cards только по `negative_candidate_rows`;164- marker cards разрешены только двух типов: `token` и `phrase`;165- `phrase` имеет приоритет над `token`;166- growth/route-like хвост не должен смешиваться с negative-review и обязан уходить в `protected_route_hold` или другой explicit hold-layer;167- user ничего не подтверждает вручную: все manual rules в этот слой приносит агент из уже просмотренных строк;168- карточка marker review обязана быть короткой: `marker`, `scope`, `matched_rows`, `cost/clicks`, `3-5 примеров`.169170Минимальный reusable запуск:171172```bash173python3 <ops-skill-root>/scripts/codex_cli_swarm_manual_review.py \174 --kind search \175 --queue <review/manual queue.tsv> \176 --project-root <client project root> \177 --merge-into <review/manual_decisions.tsv> \178 --overlay <client overlay json> \179 --local-skill <local skill path> \180 --product-catalog <product catalog path> \181 --search-rules <search rules path> \182 --lessons <lessons path> \183 --manual-decisions <existing manual decisions tsv> \184 --workers 4 \185 --chunk-size 25 \186 --model <cheap-codex-model> \187 --reasoning-effort medium \188 --sandbox danger-full-access \189 --approval-policy never190```191192Секреты, board ids и брендовые аксиомы не зашивать в global-skill.193194### Правило 0: CLAUDE.md в каждом клиентском проекте (ОБЯЗАТЕЛЬНО!)195196При инициализации ЛЮБОГО нового клиентского проекта Директа — ПЕРВЫМ делом создать `.claude/CLAUDE.md` в папке проекта со следующим содержимым:197```markdown198# Проект: [Имя клиента]199200## ЖЕЛЕЗНОЕ ПРАВИЛО201ПЕРЕД любым действием с кампаниями — СНАЧАЛА прочитай навык:202- `<ops-skill-root>/SKILL.md`203- optional project-local companion skill, если он реально существует204- `memory/lessons.md` в папке проекта (если есть)205206НИКОГДА не импровизируй со ставками, стратегиями, минус-словами.207Все решения — ТОЛЬКО на основе навыка и данных.208209## Валюта аккаунта: [KZT/RUB/BYN]210## OAuth токен: [путь к файлу]211## Логин: [client-login]212```213214Без этого файла работа с проектом ЗАПРЕЩЕНА. Файл гарантирует что агент не начнёт импровизировать, а сначала прочитает навык и lessons.215216Client overlay обязан переопределять build-layer до первого API write, если в нём заданы:217- стратегии `Search` / `РСЯ`;218- `GoalId` / `PriorityGoals`;219- гео (`RegionIds`);220- правило по минус-словам для `РСЯ`;221- формат `group names`;222- формат `DisplayUrlPath`.223- text guardrails и запрещённые термины для Direct copy.224225Если overlay противоречит generic defaults скрипта, применять overlay, а не generic defaults.226Если overlay требует `РСЯ без минус-фраз`, clearing pattern в API = `NegativeKeywords: null`; пустой `Items: []` не считать допустимым способом очистки.227228Гео-правило на будущее:229- `МО` в речи клиента неоднозначно и нельзя автоматически трактовать как `область без Москвы`;230- если клиент говорит `вся МО`, `полный МО`, `Москва и область`, использовать полный регион `RegionIds=[1]`;231- вариант `область без Москвы` допустим только при явном подтверждении и тогда задаётся как `RegionIds=[1,-213]`.232233Client-specific rule sets для discovery и review должны лежать в локальных reference-файлах проекта и подключаться через overlay.234235Минимум для reusable full-review:236- `references/product-catalog.md`237- `references/search-stop-word-rules.json`238- `references/rsya-placement-rules.json`239240Скрипты не должны знать конкретный `GoalId`, `client_key`, бренд клиента или special-case campaign id из кода. Эти значения надо брать из overlay, raw bundle или local references.241242Перед любым новым циклом работ сначала проходить `future_session_start_checklist.md`.243Если в проекте уже есть готовые артефакты и рабочие скрипты, нельзя игнорировать их и идти через импровизацию.244245Для `RSYA stop-sites` reusable-правило теперь такое:246- `Clicks >= 5 + (CTR > 1% или app/game/vpn)` = только базовый каркас;247- финальный verdict обязан учитывать ещё `Cost`, `AvgCpc`, `goal conversions`, `campaign benchmark CPA`, тип площадки и protected-platform hints;248- крупные платформы/marketplace нельзя блокировать по одному package-id;249- если есть `clicks > 0` и `cost = 0`, агент не перекладывает это на пользователя, а сам ставит действие `перепроверка raw/source -> потом стоп` или `мониторинг следующей волны`.250251Для больших `RSYA placement` очередей теперь каноничен отдельный deterministic `queue prefilter` ДО ручного verdict-слоя:252- reusable script: `scripts/prefilter_rsya_manual_queue.py`;253- это не auto-stop и не auto-verdict, а только фильтр очереди `manual review / monitor / anomaly quarantine`;254- safe-default для всех клиентов:255 - `low_signal_skip`: `conversions = 0`, `clicks < 3`, `CTR < 1%`, `cost < 20`;256 - `zero_click_tail_skip`: `clicks = 0`, `cost = 0`, `impressions < 150`;257 - `protected_low_signal_skip`: `protected/yandex`, `conversions = 0`, `clicks < 5`, `cost < 100`;258 - `app_like_low_signal_skip`: `app-like`, `conversions = 0`, `clicks < 2`, `cost < 35`;259 - `anomaly_quarantine`: `clicks > 0 and cost = 0` или `conversions > 0 and cost = 0`.260- пороги prefilter хранить в client/local rules file (`queue_prefilter`), а не в коде;261- строки из `auto_skipped` нельзя считать готовыми stop-sites: это только `monitor/skip from manual stop review`;262- строки из `anomaly_quarantine` не должны попадать ни в auto-stop, ни в обычный manual-stop shortlist до raw/source recheck.263264Если build, структура или тексты начинаются после upstream-исследования через lifecycle-слой, downstream skill обязан сначала проверить наличие трех ручных артефактов:2652661. `research/analysis/единая-карта-конкурентов.md`2672. `research/analysis/пакет-структуры-будущего-кабинета.md`2683. `research/analysis/пакет-текстов-и-офферов.md`2694. `research/analysis/готовые-тексты-для-директа.tsv`270271Если их нет, не перескакивать сразу к сборке кабинета "из головы", а сначала дособрать этот upstream handoff.272273Перед сборкой текстов или отправкой их человеку предпочтителен такой путь:2742751. ручная подготовка текстов в `готовые-тексты-для-директа.tsv`;2762. прогон через `scripts/validate_direct_copy_pack.py`;2773. только потом перенос в build-слой.278279Если задача дошла до стадии `pre-moderation`, reusable-канон теперь требует отдельного channel split:2802811. `Search` и `РСЯ` считаются разными deliverables и не собираются из одного универсального copy-pack.2822. До build/moderation handoff должны существовать отдельные артефакты:283 - `search-group-map`284 - `search-negative-bundles`285 - `search-ad-copy-pack`286 - `rsya-group-map`287 - `rsya-copy-and-image-pack`2883. Для `Search` обязательны:289 - точная группа/интент;290 - landing;291 - negative bundle;292 - text guardrails.2934. Для `РСЯ` обязательны:294 - audience;295 - trigger;296 - message angle;297 - image brief;298 - avoid-list для модерации и mismatch intent.299 - `5` уникальных ad variants на каждую группу;300 - `5` уникальных изображений на каждую группу;301 - file-first карта `group x variant -> template_id/title/image_url`, если изображения берутся из продуктовых шаблонов.3025. Нельзя переносить поисковый copy-pack в `РСЯ` без отдельной переработки под visual/message intent.3036. Нельзя считать `модерация готова`, пока этот channel split не собран и не проверен.3047. Для `Search` production default:305 - на каждую группу должно быть `ровно 5` уникальных объявлений до стадии `moderation-ready`;306 - все `5` должны соответствовать ключам внутри этой группы и заметно отличаться друг от друга по angle / promise / pain / CTA.307 - default bidding strategy = `WB_MAXIMUM_CONVERSION_RATE` с оплатой за клики;308 - default `GoalId` для стратегии = `13`, если overlay явно не задаёт другой search goal;309 - для `Search` в auto strategy нельзя оставлять `BidCeiling=null`; нужен явный `BidCeiling` / max CPC из overlay или approved bid baseline;310 - если в кампании до этого стоял manual bidding и есть `DailyBudget`, при переводе в auto strategy budget нужно переносить в weekly layer, а `DailyBudget` сбрасывать в `null`.3118. Для `РСЯ` production default:312 - на каждую группу должно быть `ровно 5` уникальных объявлений;313 - на каждую такую пятёрку должно быть `5` уникальных изображений;314 - default bidding strategy = `PAY_FOR_CONVERSION_MULTIPLE_GOALS`;315 - default optimisation intent для `РСЯ` = все approved lead goals клиента: звонок / форма / messenger, если пользователь не задал иной shortlist;316 - для unified `РСЯ` с несколькими целями использовать exact enum `PAY_FOR_CONVERSION_MULTIPLE_GOALS` и nested block `PayForConversionMultipleGoals`;317 - `PriorityGoals` должны содержать все approved lead goals клиента; без них multi-goal стратегия невалидна;318 - single-goal `PAY_FOR_CONVERSION` допустим только как явно согласованный fallback; в нём обязателен `GoalId` и `Cpa`;319 - `WB_MAXIMUM_CLICKS` в `РСЯ` нельзя ставить по умолчанию.320- если пользователь просит брать изображения из шаблонов, default source = `последние реальные template covers` из live-каталога/БД, а не выдуманные concept-art placeholders.321- если изображения берутся с сайта клиента, брать их только с соответствующей landing/page этого кластера; переносить фото между чужими посадочными нельзя.322- `DisplayUrlPath` не должен быть техническим id/slugs вида `p1-*`, если у клиента нет такого явного правила. Default = человекочитаемый путь.323- `DisplayUrlPath` должен проходить build-time валидацию: человекочитаемый, без технических префиксов и длиной не более `20` символов.324- имена групп в кабинете не должны оставаться техническими кодами, если не требуется служебная отладка. Default = человекопонятные имена.325- если клиент явно требует `РСЯ` без минус-фраз, build-layer не должен автоматически протаскивать generic negative bundles в `РСЯ`.326- если клиент задаёт разные правила оптимизации для `Search` и `РСЯ`, global skill обязан сохранить channel-specific split и не пытаться выровнять стратегии между каналами.327- в текстах Direct слово `WhatsApp` запрещено: не использовать его в `Title`, `Title2`, `Text`, `callouts`, `sitelink titles/descriptions`.328- если на сайте есть WhatsApp как факт, в Direct copy использовать нейтральные замены типа `быстрая связь`, `связь с менеджером`, `быстрый расчет`.329- статус `live / включено` нельзя объявлять по одному `ResumeResults`. После `campaigns.resume` обязателен свежий `campaigns.get` с проверкой `State`, `Status`, `StatusPayment`, `StatusClarification`.330- если после `resume` кампания остаётся `State=OFF`, skill обязан назвать точный blocker из live API и не писать, что запуск завершён.331332Товарный или фидовый слой не считать обязательной частью стандартного пакета.333Добавлять его только по прямому запросу пользователя или после отдельного решения в клиентском документе.334335Для competitor-review reusable-слой обязан хранить не только лидирующие домены, но и сами тексты объявлений конкурентов:336- отдельный generated file с колонками `query / region / domain / title / snippet / url`;337- HTML-отчёт обязан показывать этот слой напрямую, а не только счётчики появлений.338339Для клиентского веб-отчета канонический путь теперь такой:3403411. подготовленные ручные артефакты;3422. машинные рендеры таблиц;3433. HTML-страница без ссылок на внутренние markdown-файлы;3444. `build_secure_client_report.py`;3455. локальная проверка;3466. mobile check `390px`;3477. live check после деплоя.348349## Client Overlay Contract350351По умолчанию навык ищет локальный файл клиента в таком порядке:352- `./.codex/yandex-performance-client.json`353- `./claude/yandex-performance-client.json`354- `./.claude/yandex-performance-client.json`355- путь из `YANDEX_PERFORMANCE_CLIENT_CONTEXT`356357В public bundle client overlays не хранятся. Они должны жить в локальном private project-layer вне git, например:358- `./.codex/yandex-performance-client.json`359360Шаблоны:361- [client_context.example.json](templates/client_context.example.json)362- [routing_map.example.tsv](templates/routing_map.example.tsv)363- [campaign_id_map.example.json](templates/campaign_id_map.example.json)364- [copy_map.example.json](templates/copy_map.example.json)365366Описание полей:367- [local_overlay_contract.md](references/local_overlay_contract.md)368- [yandex_cloud_search_handoffs.md](references/yandex_cloud_search_handoffs.md)369370Быстрый scaffold:371```bash372python3 <ops-skill-root>/scripts/init_client_context.py \373 --output ./.codex/yandex-performance-client.json \374 --client-key acme375```376377## Источники данных и иерархия истины3783791. Direct API / Reports API / Wordstat API / Roistat API / Metrika API3802. Локальные raw-файлы, собранные скриптами3813. Локальный client overlay3824. Локальные client-specific skills/docs3835. Markdown-документация и агентские выводы384385Если live-data конфликтует с локальными доками, верить live-data.386Если пользователь сказал что локальные raw-выгрузки устарели, пункт `2` временно исключается из принятия решений до нового live-сбора.387388## Жёсткие правила3893901. `ПАРСИНГ != АНАЛИЗ`391- парсинг только официальными API и скриптами;392- анализ делать только вручную мной по raw-файлам;393- не домешивать новые API-вызовы в фазу анализа.394- если в локальном проекте есть `build_decision_report.py`, `build_executive_review.py`, `build_executive_html.py` или похожие рендеры, они не имеют права повышать `review/generated/*safe_ready.tsv` до пользовательского статуса `ready_now`. User-facing verdict слой обязан идти только из `review/manual/*.tsv` и, если проект хранит решения отдельными файлами, `review/manual_decisions/*.tsv`.395- если ручной verdict по строке не внесён, верхний слой обязан прямо показывать `manual gate incomplete` / `manual_verdict_required`, а не маскировать machine shortlist под готовый пакет правок.396- reusable queue-builders обязаны поддерживать и legacy raw paths, и новые `*_v2/raw_bundle/*` пути; path drift не оправдывает переход назад к machine-only verdict.397- scripts имеют право только:398 - собирать;399 - чистить;400 - нормализовать;401 - сортировать;402 - чанковать;403 - рендерить данные.404- scripts не имеют права:405 - ставить вердикты;406 - предлагать стоп-слова, стоп-площадки, рост, ставки, новые группы, мониторинг;407 - решать что target / non-target вместо ручного построчного анализа.408- analysis-скрипты для классификации ключей, фраз, минус-слов и масок запрещены.409- анализ ключевых слов из Wordstat, SQR и Roistat делать без скриптов, только вручную по raw-выгрузкам.410- upstream research по SERP/footprint/competitor pages тоже не собирать вручную по одной фразе.411 Default path = batch job-spec + collector script.412 Для этого использовать:413 - `<client-lifecycle-root>/scripts/yandex_search_batch.py`414 - `<client-lifecycle-root>/scripts/yandex_search_ads_batch.py`415 - `<client-lifecycle-root>/scripts/build_domain_shortlist_from_serp.py`416 - `<client-lifecycle-root>/scripts/firecrawl_scrape.py --jobs-file ...`417 - `<client-lifecycle-root>/scripts/build_followup_jobs_from_serp.py`418 - `<client-lifecycle-root>/scripts/split_tsv_batch.py`419 - `<client-lifecycle-root>/scripts/merge_sitemap_batch_outputs.py`420 - `<client-lifecycle-root>/scripts/render_serp_wave.py`421 - `<client-lifecycle-root>/scripts/render_ad_serp_wave.py`422 - `<client-lifecycle-root>/scripts/render_sitemap_candidates.py`423 - `<client-lifecycle-root>/scripts/render_page_capture_inventory.py`424- полный competitor collection строить не от случайных стартовых запросов, а от вручную валидированного keyword set.425 Допустим ранний scout/reconnaissance для проверки рынка и пайплайна, но exhaustive `organic SERP` / `ad SERP` waves запускаются только после этапа:426 - official `Wordstat` raw;427 - ручная валидация масок, ключей и минус-логики;428 - формирование job-matrix `keyword x geo`.429- для каждого validated keyword нужно сохранять raw-query trail и потом расширять найденные домены через sitemap/page-capture.430- до follow-up сборов сначала строить таблицу повторяемости доменов и вручную утверждать укороченный shortlist.431 Default path на будущее:432 - брать `топ-15` повторяющихся доменов из подтвержденной выдачи Яндекса;433 - до shortlist-builder-а механически исключать очевидные некоммерческие URL-паттерны: статьи, новости, справочники, PDF;434 - не тянуть `sitemap/page-capture` по длинному хвосту слабых доменов.435- после live `organic SERP` wave follow-up jobs должны тоже строиться скриптом, а не вручную:436 - `serp_results.tsv -> page-capture-jobs.tsv`437 - `serp_results.tsv -> sitemap-jobs.tsv`438 - затем только batch collectors по этим job-файлам.439- если batch слишком большой или медленный, разбивать jobs нужно тоже скриптом:440 - `split_tsv_batch.py` для chunk-files;441 - затем несколько collector workers по chunk-TSV;442 - затем merge/normalize step скриптом, без ручной склейки.4434442. Wordstat только официальный445- запрещён веб-скрейп Wordstat;446- `numPhrases=2000` обязателен для полного охвата масок.447- канонический порядок всегда такой:448 - `СТРУКТУРА -> МАСКИ -> РЕВЬЮ МАСОК -> ПАРСИНГ СКРИПТОМ -> [полный успех] -> АНАЛИЗ -> ЧИСТКА -> ГРУППИРОВКА`;449 - нельзя перепрыгивать из масок сразу в анализ;450 - нельзя делать one-off `wordstat_*` вызовы вместо wave-collector workflow.451- до любого парсинга обязателен `product map`:452 - официальные названия;453 - разговорные названия;454 - тендерные/закупочные формулировки;455 - жаргон;456 - аббревиатуры;457 - ошибки написания;458 - латиница/кириллица;459 - применения по отраслям.460- `Wave 1` обязан начинаться с `L1` root-масок.461 Где это семантически возможно, root-маски должны быть однословными.462- после `L1` в тот же `Wave 1` добавляются `L2` product masks.463 Для нового круга правил:464 - `Wave 1` в `9/10` случаев строится на однословных масках;465 - `Wave 2` допускает двухсловные маски;466 - трехсловные маски не считать default path без явной причины.467- в клиентском или внутреннем отчете слой спроса из Wordstat нужно показывать отдельно:468 - широкие корневые маски как обзорный ландшафт;469 - точные базовые маски как рабочий слой;470 - использовать только `totalCount` по маске;471 - не суммировать вложенные запросы и не складывать маски между собой как единый объем рынка.472- но для ручного анализа `totalCount` недостаточен:473 - по каждой approved mask надо собирать полный официальный ceiling `2000 строк = 40 страниц`;474 - затем лично просматривать каждую строку `topRequests` и `associations`;475 - только после такого row-by-row review разрешено выделять `target`, `new mask`, `adjacent`, `stop-candidate`, `noise`.476- для SQR manual-review разрешены только два вида deterministic propagation из manual-approved слоя:477 - если стоп-слово уже подтверждено вручную в прошлой или текущей волне, можно автоматически убрать из новой очереди unresolved-строки, где это exact single-word минус встречается в `query`/`criterion`;478 - это не новый verdict, а dedupe/preprocessing;479 - если уже внесён ручной verdict c exact query / exact token / exact phrase внутри конкретного `ad_group_name`, можно строить `manual-approved rulebook` и детерминированно распространять это решение на unresolved-строки только при exact/scope-safe match;480 - такой propagation не придумывает новый action: он берёт только уже утверждённый `assistant_action`/`assistant_reason` из manual decision;481 - конфликтующие matches не auto-apply, а уходят в отдельный conflict-файл;482 - любой skip/propagation обязан идти с audit-файлами `что исключено`, `rulebook`, `auto-decisions`, `conflicts`, `remaining`.483- если full row-by-row manual-review Search-хвоста становится неэкономным, перед любым swarm/manual escalation разрешён только один дополнительный deterministic слой:484 - `search_negative_marker_engine.py`;485 - он не имеет права принимать verdict за строку;486 - он имеет право только:487 - bootstrap already-approved `exclude` и `park_growth` rules;488 - автоматически вычитать строки, уже покрытые этими правилами;489 - автоматически парковать `growth/route/protected` хвост вне negative-review;490 - строить компактные marker cards по оставшемуся `negative_candidate` слою.491- канонические выходы этого слоя:492 - `search_excluded_by_marker_rules.tsv`493 - `search_growth_hold.tsv`494 - `search_protected_route_hold.tsv`495 - `search_negative_candidate_rows.tsv`496 - `search_negative_marker_cards.tsv`497 - `search_negative_marker_examples.tsv`498- good-state для этого слоя:499 - active negative review становится на порядок меньше raw queue;500 - целевые хвосты типа `потолки/LED/скрытый монтаж/парящий профиль/плинтус` не попадают в marker cards только потому, что там встретился случайный модификатор;501 - явный non-target хвост (`другой товар`, `B2B`, `чужой бренд`, `marketplace`, `alien use-case`) остаётся в negative candidates.502503Для Search negatives перед live apply теперь обязателен отдельный dry-run слой, а не только validation JSON:504- reusable script: `scripts/dry_run_search_negatives_pack.py`;505- он читает уже подготовленный `search_negatives_pack_apply.json`, снимает live `NegativeKeywords` по adgroup и проверяет:506 - drift между live baseline и `before_keywords` из pack;507 - сколько минусов уже стоят в группе;508 - сколько реально будет добавлено после merge;509- если есть drift, status должен быть `blocked`, а live apply запрещён до пересборки pack;510- canonical outputs: JSON + text report с `drift_count`, `dry_run_add_count`, `skip_existing_count`.511512Для RSYA apply теперь канонический hard blocker такой:513- если validation/analysis слой всё ещё содержит `manual_review_count > 0`, `validation_pack_rsya.py` обязан ставить status=`blocked`;514- `prepare_apply_rsya_excluded_sites.py` обязан повторно hard-fail'ить, если в validation summary не ноль manual tail;515- правило простое: `RSYA not touch until manual tail is closed`.516- если пользователь просит `просмотреть поисковые фразы`, `собрать минус-фразы`, `разобрать SQR`, `посмотреть новые поисковые фразы` или явно требует `вручную каждую строку`, default path только такой:517 - свежий live/raw сбор;518 - полный ручной row-by-row review каждой строки;519 - сохранение verdict-слоя в `review/manual/*.tsv` или эквивалентный manual-layer;520 - сбор decision table/report;521 - reduction-layer: phrase-level evidence из manual-layer обязано быть сведено к production-safe stop-words / коротким safe-маскам на нужном scope;522 - отдельный validation-layer: `single-token only` или явно одобренные короткие safe-маски, плюс conflict-check с target words;523 - и только потом pre-apply pack из `approved_negative`.524- если объём manual-review слишком велик и user явно разрешил swarm:525 - резать очередь на bounded chunks;526 - запускать несколько локальных `codex exec` воркеров на `<cheap-codex-model>` с `model_reasoning_effort="medium"` по умолчанию;527 - для escalation / conflict-validation / final QA поднимать более сильную модель (`<strong-codex-model>` или project-approved codex model) только на спорный хвост;528 - для local file-only review по умолчанию НЕ копировать пользовательский `config.toml` в worker `CODEX_HOME`, чтобы воркеры не поднимали лишние MCP-серверы и не тратили токены на startup-шум;529 - промт воркера обязан ссылаться на global skill, local skill, overlay, product catalog / local rules, existing manual decisions и chunk TSV;530 - worker не имеет права редактировать master queue / master decisions напрямую, только вернуть schema-valid JSON;531 - launcher обязан провалить chunk, если `candidate_id` coverage неполный, есть extra ids, есть duplicates или пустые `assistant_action` / `assistant_reason`;532 - merge в `manual_decisions.tsv` разрешён только после такого validation pass.533- запрещено начинать такой workflow с:534 - machine shortlist;535 - `safe_ready`;536 - auto-mined stop words;537 - broad phrase collapse;538 - удаления уже добавленных или новых поисковых фраз до ручного verdict по строке.539- если в кабинете уже есть добавленные фразы, новые поисковые фразы или ранее залитые минуса, это не повод механически их убирать.540 Сначала вручную смотреть сырые строки поисковых фраз, потом принимать решение по exact query/token/phrase.541- live apply/rollback по SQR-минусам заблокирован, пока manual gate не закрыт полностью.542- дубликаты можно схлопывать только после ручного verdict по exact query.543 Нельзя сначала схлопнуть хвост, а потом делать вид, что вся группа строк уже просмотрена вручную.544- phrase-level evidence не равно production-ready минус-фраза.545 Фразы из manual SQR review нельзя лить в кабинет как есть, если из них можно безопасно выделить короткий блокирующий токен или короткую safe-маску.546- канонический production-layer для SQR-negatives:547 - `review/manual/*` = evidence и verdict;548 - `review/manual_reduced/*` или эквивалент = сокращённые stop-words / safe-маски;549 - `live_apply/*negative_tasks*.tsv` разрешён только из reduced-layer.550- reusable apply-path по умолчанию обязан отклонять tasks, где negative params содержат `phrase`, если нет отдельного explicit override от пользователя и письменного объяснения, почему token-reduction невозможен.551- user-facing/client-facing отчет обязан различать:552 - `по каким поисковым фразам нашли проблему`;553 - `какие короткие стоп-слова или safe-маски реально добавили`.554 Нельзя выдавать phrase-level evidence за список реально добавленных production-stop-слов.555- канонический renderer для этого слоя:556 - `scripts/render_wordstat_mask_demand.py`557 - вход = config TSV с approved masks и путями к raw;558 - выход = `wordstat-demand-exact.tsv`, `wordstat-demand-roots.tsv`, `_summary.json`559- обязательные соседние renderer-слои:560 - `scripts/render_wordstat_seasonality.py`561 - `scripts/render_wordstat_geo.py`562 - выход = `wordstat-seasonality-matrix.tsv`, `wordstat-geo-priority.tsv`, `_summary.json`563- канонический collector обязан уметь собирать и эти raw-слои:564 - `--dynamics true`565 - `--regions-report true`566 - `--regions-tree true`567- если для сезонности или географии возникает соблазн сделать разовый `wordstat_*` вызов вручную, это считать нарушением workflow.568 Сначала расширять или переиспользовать `scripts/wordstat_collect_wave.js`.569- после составления `Wave 1` обязателен отдельный `mask review`:570 - web/source synonym review;571 - Wordstat association review на широких масках;572 - только потом запуск collector-а.5735743. `Pre-moderation` = отдельный gate, а не хвост build-этапа575- до `ads.moderate` должны быть собраны и проверены:576 - отдельный `Search` pack;577 - отдельный `РСЯ` pack;578 - channel-specific negatives / intent guards;579 - moderation-safe promises;580 - image brief / actual creatives для `РСЯ`;581 - live-readiness checks.582- если чего-то из этого нет, статус должен оставаться `handoff-ready`, но не `moderation-ready`.583- `Wave 1` и `Wave 2` обязательны.584 `Wave 2` строится из gap-analysis по итогам `Wave 1`, а не угадыванием “что еще спросить”.585- парсинг Wordstat допустим только reusable collector-ом из `masks-file -> raw files`.586 Парсинг вручную по одной маске через MCP/tool вызовы запрещён.587- канонический Wordstat entrypoint на этом маке:588 - `bash <ops-skill-root>/scripts/wordstat_tool.sh preflight ...`589 - `bash <ops-skill-root>/scripts/wordstat_tool.sh collect-wave ...`590 - `bash <ops-skill-root>/scripts/wordstat_tool.sh preflight-save ...`591 - `bash <ops-skill-root>/scripts/wordstat_tool.sh collect-wave-save ...`592- discovery order для Wordstat всегда такой:593 - global wrapper `<ops-skill-root>/scripts/wordstat_tool.sh`;594 - global `wordstat_preflight.sh` / `wordstat_collect_wave.js`;595 - только потом project-local fallback из `.claude/skills/direct-search-semantics/scripts/`.596- локальные project scripts нельзя молча считать primary path, если global canonical wrapper доступен.597- режим по умолчанию для агентской работы = `file-first`:598 - raw, summaries, logs и render outputs сначала сохранять в файлы;599 - в контекст не вытаскивать сырые rows/JSON, если это не нужно для точечной проверки;600 - после сбора открывать уже сохранённые `.tsv/.json/.md` частями через `sed/head/rg`.601- до анализа обязателен completeness gate:602 - число raw-файлов должно совпадать с числом масок;603 - пустые/ошибочные raw-файлы должны быть выявлены;604 - новые маски из associations должны быть вынесены в gap/wave2 backlog.605- analysis-скрипты для классификации Wordstat-ключей и минус-слов запрещены.606 После полного raw collection анализ делать только вручную агентами/оператором по raw bundle.607- Не считать Wordstat автоматически только `OAuth`-задачей или только `Cloud`-задачей.608- Сначала нужно live-проверкой определить, какой официальный путь реально доступен клиенту:609 - существующий legacy OAuth-app path;610 - или `Yandex Cloud Search API -> Wordstat`.611- Если legacy path исторически работал у клиента, его нельзя отбрасывать без проверки.612- Если `oauth` токен содержит `wordstat:api`, но live collector получает `403 Forbidden`, это не считать просто "нужно заново авторизоваться".613 Нужно проверить:614 - корректный method/header;615 - не упирается ли проект в `ClientId/app approval`;616 - не нужен ли переход на cloud-path.617- Если preflight написан на `httpx`, не использовать `response.ok`: у `httpx` authoritative-флаг успеха это `response.is_success`.618- Operator-facing Wordstat status должен различать:619 - внутренний баг интеграции;620 - `blocked` по `401/403`;621 - `ready`.622 Нельзя показывать человеку общее `failed`, если live diagnostics уже доказывают конкретный `blocked` verdict по endpoint checks.623- Для cloud-варианта заранее фиксировать:624 - `folder_id`625 - auth mode (`API key` или `IAM token`/service account)626 - роль на сервис-аккаунте `search-api.webSearch.user`627 - какой именно Search API endpoint используется в collect628629…(truncated)