Follow-up по #581 / эпик #584 (находка #4 из аудита — самый объёмный пласт, ~1400 строк).
Распространить уже доказанный _TextStrategyConfig (NamedTuple) + _build_text_strategy_block на остальные типы кампаний: каждая пара build_*_search_strategy/build_*_network_strategy дублирует весь пайплайн (field_support, валидация detail-флагов, CPA-обработка, сборка budget-блоков).
Разбить по одному PR на тип кампании (каждая пара ~350 строк дублей → отдельный PR ≤1000 добавленных):
- dynamic_text, smart_campaign, unified_campaign, mobile_app.
Заодно (находка reuse-ревью #581): переиспользовать новые _build_custom_period_budget/_build_exploration_budget в inline budget-валидации dynamic/smart/mobile builder'ов (сейчас там свой паттерн *_values dict + len != len проверка). Не забыть exploration_yes_only=False для этих поверхностей (по cached WSDL).
Инварианты: --help, --dry-run payload, тексты ошибок байт-идентичны. Эталоны до/после по каждому типу кампании. Гейты: test_dry_run.py, test_wsdl_parity_gate.py, test_cli_contract.py. НЕ трогать реестр CAMPAIGN_TYPE_BUILDERS и build_*_base (точки расширения).
Follow-up по #581 / эпик #584 (находка #4 из аудита — самый объёмный пласт, ~1400 строк).
Распространить уже доказанный
_TextStrategyConfig(NamedTuple) +_build_text_strategy_blockна остальные типы кампаний: каждая параbuild_*_search_strategy/build_*_network_strategyдублирует весь пайплайн (field_support, валидация detail-флагов, CPA-обработка, сборка budget-блоков).Разбить по одному PR на тип кампании (каждая пара ~350 строк дублей → отдельный PR ≤1000 добавленных):
Заодно (находка reuse-ревью #581): переиспользовать новые
_build_custom_period_budget/_build_exploration_budgetв inline budget-валидации dynamic/smart/mobile builder'ов (сейчас там свой паттерн*_valuesdict +len != lenпроверка). Не забытьexploration_yes_only=Falseдля этих поверхностей (по cached WSDL).Инварианты:
--help,--dry-runpayload, тексты ошибок байт-идентичны. Эталоны до/после по каждому типу кампании. Гейты:test_dry_run.py,test_wsdl_parity_gate.py,test_cli_contract.py. НЕ трогать реестрCAMPAIGN_TYPE_BUILDERSиbuild_*_base(точки расширения).