Все версии

Историческая редакция. Решение о флаге кампании отменено. Актуальный контракт: SYSTEM-DESIGN.md, CTV управляется только в админке арендатора.

UX-контракт кампаний и аудиторий

1. Информационная архитектура

Рабочий список кампаний сохраняет advertiser/период/поиск/вид в URL. Строка кампании: статус доставки, CTV вкл/выкл, бюджет/период, число флайтов/групп, источник аудиторий, непубликованные изменения. Дерево слева от редактора: кампания, флайты, группы. Выбранная сущность и ее effective revision видны всегда. Форма справа: Основное / Закупка / Аудитории / CTV / Объявления / Изменения. При переходе на дочерний уровень неизмененные поля показывают источник и значение.

Сохранить черновик не меняет закупку; опубликовать применяет проверенный diff. Запуск/пауза отдельны от публикации. Перед публикацией: changed fields, scope, дочерние зависимости, CTV applicability, аудитории/версии/ошибки. Unsaved guard при закрытии/переходе: остаться, сохранить черновик, уйти без сохранения. Autosave не объявляется успешным до ответа сервера; ошибка оставляет ввод.

2. Флаг CTV: исчерпывающий gate

Canonical proposal: campaign.ctvEnabled boolean; единственный родительский источник для всех флайтов/групп. Новый campaign default false. Для существующих кампаний миграция анализирует текущую конфигурацию и сохраняет поведение; нельзя всем молча назначить false и отключить действующую закупку.

| Состояние | Поведение | | --- | --- | | false | Все CTV inputs/selects/searches/list actions/toggles disabled; значения читаются, но не применяются | | true | Все CTV controls доступны по флагу; ошибки значений показываются у полей, не блокируют редактирование остальных CTV-полей | | Нет права редактирования | Весь редактор read-only независимо от значения флага; права не подменяются CTV-переключателем | | Отключение в черновике | Локальный impact preview, подтверждение, сохранение CTV-набора без применения | | Отключение опубликованной кампании | Подтверждение с affected descendants; реальный эффект только после публикации revision | | Повторное включение | Восстановить сохраненные значения и проверить актуальность словарей/совместимость; не менять бюджет или стратегию автоматически | | Child при false | Тот же disabled набор, подпись "CTV выключен в кампании" и ссылка к источнику; локально включить нельзя |

Реестр ctv-fields.json - список элементов UX, который при внедрении связывается с фактическими schema paths. Запрещены обходы через deep link, copy, bulk, import template, API и логические выражения с CTV capabilities. Backend и runtime игнорируют неактивный сохраненный CTV-набор; попытка менять активные CTV-поля при false отклоняется validation error. Неактивный набор - отдельная сущность данных, а не активный targeting, который frontend просто спрятал.

Выключение в этой концепции также удаляет CTV из разрешенного активного инвентаря. Это явное предложенное продуктовое правило: флаг не должен оставлять скрытую CTV-закупку без доступных настроек. Включение разрешает CTV, но не превращает кампанию в CTV-only: Web/App и их параметры остаются. Перед выключением показать потерю доступности CTV placements и CTV-only групп, не удалять эти группы молча. В CTV-only группе после выключения будет blocking readiness с причиной; пользователь либо меняет инвентарь, либо оставляет группу на паузе.

Не все видео/контентные поля являются CTV: бюджет, CPM, частота, общие гео, Web/App bundles, mobile devices, обычный VAST/video и generic IAB категории продолжают работать. TV-specific каналы/жанры/TV bundles/device options/pod и предложенные Live/VOD/SSAI controls находятся именно в CTV scope. Если существующий schema field общий для Web/App/CTV, не блокировать его целиком: выделить CTV-only options или отдельный namespace. Проверка capabilities, не поиск "tv" в названии. Словарь с нулем значений - empty state при доступном поиске, не второй gate.

Не удалять сохраненные CTV-настройки при false; explicit очистка отдельна и требует подтверждения. В макете ее нет в основном сценарии. При CTV=false валидация неактивных значений не блокирует публикацию Web/App; при включении ошибочные восстановленные значения требуют исправления до публикации.

3. Аудитория как сущность и как связь

Библиотека рекламодателя владеет Segment (name, description, source, ID type, retention, published version, history, last size/time, status). В кампании редактируется Binding (segmentId, version, include/exclude, ANY/ALL, scope). Кнопка "Редактировать аудиторию" открывает entity editor с impact по использованию, а "Изменить применение" - только текущую связь. Заголовки и primary actions разные.

Опубликованные версии списка immutable. Изменение данных создает draft v7 рядом с действующей v6; после validation v7 готова к явному применению. По умолчанию новые связи pinned к опубликованной версии. Публикация v7 не переключает все кампании с v6: отдельное применение/массовое обновление с preview. Для событийного источника pinned означает версию правил/схемы, а membership меняется по окну/TTL; размер всегда с timestamp, не обещает постоянное множество пользователей.

Нельзя поменять тип ID/source существующей используемой аудитории незаметно: предложить "Создать копию с другим типом". Имя/описание - метаданные; membership, TTL и source rules - versioned изменения с пересчетом и impact. Права на metadata, members и bindings разделимы. В UI отсутствующая аудитория не исчезает из связи: показать неизвестную/недоступную ссылку и ее blocking reason.

ANY: A OR B; ALL: A AND B. Исключения всегда вычитаются после включений. Пересечения не складываются для оценки размера. Пустой include с явным "Без ограничения аудитории" означает всех совместимых пользователей; пустое выбранное/рассчитанное множество означает никого, а не всех. Missing/expired/failed source не трактуется как отсутствие ограничения: неопределенность/blocked readiness, последняя пригодная версия только по явной политике stale grace из API с подписью. Кампания не расширяется молча.

4. Загрузка списков

Путь: выбрать аудиторию/новую, выбрать файл или вставить список, тип ID, mapping, режим append/replace, validation preview, подтвердить, job, новая версия, применить. Файл CSV/TXT; quoted CSV разбирается parser, не split(','). Encoding, delimiter, header, колонка, тип ID и правила нормализации видны. Текущий UI лимит 40 MB - наблюдение, целевой лимит получает клиент от API. Большой файл обрабатывает job, не один огромный JSON-массив в браузере. Drag/drop и обычный file button равноправны.

Replace заменяет только выбранный тип ID, не все ID types сегмента, как в текущем API. Append объединяет с существующими, duplicates не создают новых ID. Preview показывает rows/header/empty/invalid/duplicates/unique/new/alreadyPresent/finalCount. Append final нельзя получить сложением uploadCount и oldCount без server intersection. Если old snapshot/target version поменялся, новый preview; не использовать устаревший diff.

Ошибочные строки не применяются автоматически: исправить файл либо явно выбрать "Пропустить N ошибочных строк". Скачать отчет: номер строки/тип причины, ID маскированы. Не показывать raw emails/phones в таблице/логах; hashing/normalization на сервере по согласованному типу. Прототип не реализует hashing и не делает отправку.

Apply атомарный: старый ready snapshot используется до успешного commit новой версии. Cancel до commit, retry по idempotency key, upload resume/checkpoint; после commit "отмена" - новый rollback version/возврат binding, а не уничтожение истории. Progress по байтам и этапу; 100% upload не равно 100% processing. Частичные результаты/error report доступны, данные формы не теряются при потере сети.

5. Наследование блока аудиторий

Цепочка: campaign -> flight -> ad group. У рекламодателя библиотека и обязательные исключения, если такая политика настроена; не выдавать это за готовый API. Наследуется только блок аудиторий, не весь targeting. Parent revision/source видны. Обязательные исключения advertiser остаются при любом child режиме.

| Режим | Effective правило | UX | | --- | --- | --- | | INHERIT | P | Live follow опубликованного parent block; поля readonly, источник и итог видны | | NARROW | P AND L AND NOT X | Parent constraints не редактируются; local include/exclude дополняют и сужают | | REPLACE | L AND NOT X AND NOT M | Собственный блок; обычные parent условия отключаются, mandatory M сохраняются; право и impact обязательны |

P уже содержит parent exclusions/mandatory. L - ANY/ALL локальных сегментов, X - локальные исключения. В REPLACE не наследуются обычные parent ограничения; это может расширить охват и требует permission/явного подтверждения. На root режима INHERIT нет, есть собственный блок/явное отсутствие ограничения. В NARROW без local include L=all; пустой материализованный сегмент L=empty.

"Скопировать и настроить" создает REPLACE snapshot с parent версиями и предупреждением о разрыве follow; это не live inheritance. "Вернуть наследование" удаляет локальную конфигурацию после diff/подтверждения, не удаляет сами сегменты из библиотеки. Parent publish preview перечисляет inherited/narrow children и unaffected replaced. Narrow child не может снять parent exclusion; свои parent-overrides не молча. Конфликт parent/child revision требует recompute impact до commit.

6. Массовые операции: две области

Библиотека: выбрать аудитории -> обновить совместимые источники, назначить retention для версии, архивировать неиспользуемые, применить/обновить связи. Кампании/дерево: выбрать целевые campaigns/flights/groups -> добавить include, добавить exclude, убрать локальную связь, заменить блок, вернуть inheritance, переключить version. Разные operation labels: "убрать из группы" не "удалить аудиторию".

По умолчанию выбор текущей страницы; select-all-results отдельное явное действие с total, query snapshot и exclusions, а не скрытые чекбоксы. Выбранные аудитории и цели показаны отдельными списками. Scope всегда один advertiser; cross tenant и несовместимые ID запрещены. Mixed CTV/rights/source/provider states видны. Унаследованные связи нельзя remove локально: вместо этого изменить источник или явно выбрать REPLACE/NARROW. Bulk никогда не разрывает inheritance неявно. При выборе родителя и потомков preview объединяет унаследованные эффекты и не выполняет их дважды.

Preview: eligible/skipped/blocked/unchanged и причина на каждую цель, итоговая формула, CTV gate и версии. Применить только eligible - явная кнопка с количеством; blocked не исчезают из отчета. Add = set union с дедупликацией, повтор не удваивает. Replace/return inheritance/широкий scope требует дополнительного подтверждения. Режим применения выбирается явно: подготовить черновики или опубликовать проверенные связи. В обновленном макете по умолчанию готовятся черновики; публикация выбирается отдельно. Она требует права публикации и готовности каждой цели, не меняет статус запуска кампании. Если выбраны черновики, результат не объявляет изменения действующими. Серверный job идемпотентен, проверяет права/версии при исполнении. Атомарность на одну цель; операция на всем наборе может завершиться частично. Транзакция всего набора допустима только если явно поддержана контрактом, не обещана UX.

Результат: succeeded/failed/skipped/unchanged по ID и job ID; retry только failed с новым актуальным preview. Компенсирующая отмена успешных изменений проверяет current version, не перезаписывает более позднее чужое редактирование. Archive использованных аудиторий не молча отключает кампании: blocked+bindings, требуется сначала мигрировать связи. Source refresh в bulk доступен только для поддерживаемых providers, массовый импорт разных файлов - manifest/file-to-target mapping, а не заливка одного файла во все сегменты без подтверждения.

7. Ошибки, ревизии и публикация

Draft stores base campaign/parent/audience versions. Publish review показывает diff, затронутые child, неактивные CTV значения, affected audience versions и readiness blockers. При concurrent edit 409/412: локальные изменения сохраняются, показать current/base/local по полям, выбрать server/local для конфликтующих, пересчитать preview; нет кнопки безусловно затереть чужое.

Нельзя публиковать неопределенный effective audience, неизвестную активную версию или активные CTV values при ctvEnabled=false. Очевидные stale/late ответы не перерисовывают измененный ввод. Таймаут commit требует проверить job/idempotency status, а не повторить действие вслепую. Аудит хранит автора/область/до/после и ссылку на job; raw members/secrets в журнал не попадают.

8. Предметные критерии будущей реализации

  1. CTV gate: все registry поля на campaign/flight/group, deep links/bulk/API/copy и expressions; off не применяется, on доступно, off/on roundtrip без потерь; старые кампании совместимы, CTV-only child имеет явный readiness blocker.
  2. Audience entity/binding: изменения сегмента не затрагивают pinned bindings без явного применения, versions/source/ID types/TTL и права проверены сервером.
  3. Import: CSV quoting/header/mapping/encoding, append/replace по ID type, duplicates/intersections/invalid policy/large files/cancel/retry/atomic commit.
  4. Inheritance: grandparent chain, inherit/narrow/replace/mandatory exclude, empty != unset, unknown/error != all, parent revisions/recompute/impact.
  5. Bulk: page/all-results, mixed eligibility, inherited links, per-target transaction, stale version/revoke, idempotency, partial result/retry/compensating undo.
  6. Accessibility/state: 1440/1024/390/320, темы, keyboard/focus/dialog focus trap, long names/empty/loading/error/readonly/unsaved/conflict, no overflow документа.

Здесь определены критерии и макеты, не результаты тестов приложения. Тесты реализации писать по задачам, запускать общим test-all по правилам проекта.

9. Дополнение дизайн-ревью 08.11.2026

Экран объявления и подбор материалов - часть пути кампании. Неодобренные материалы не выбираются, отсутствие готовых блокирует публикацию. Audience picker обязательно получает scope campaign/group и возвращает связи только в этот scope. Выбор из группы не меняет campaign bindings. Данные формы между вкладками единые, diff строится из фактического черновика. Уход из измененного редактора предлагает остаться/сохранить/отказаться, а переходы между вкладками сохраняют ввод.

Ревью добавило экраны 19-32, каталог состояний и мобильный drawer. Полный журнал и границы наблюдений: REVIEW.md. Продуктовые API этим дополнением не реализованы.

10. Визуальная редакция upload

Одиночный и пакетный выбор используют один компонент с обычной кнопкой и скрытым нативным input. Карточка файла, размер, удаление, ошибки, focus/drag, progress и disabled представлены в общем стиле. Состояния - экран 33. Неподдерживаемый файл сохраняет предыдущий выбор, очистка пакета не возвращает примеры. Локальный лимит прототипа 1 MB явно показан; API и лимиты продукта не менялись. Подробности: UPLOAD.md.