Задача: MLF-296 — «Сценарии: A/B-split нода +
цели/конверсии (exit-on-goal) с атрибуцией на ветку». Это шаг 1 карточки (ресёрч), его
результат ложится в шаг 2 (макеты ноды сплита RANDOMIZER и конфигуратора цели GOAL) и
согласование с М. Терентьевым.
Дата: 2026-07-16
Метод: deep-research harness (5 углов → ~90 источников → извлечение фактов →
3-голосовая adversarial-верификация, 25 ключевых утверждений подтверждено). Финальный
форматтер харнесса упал на structured-output — синтез собран вручную из
верифицированного журнала.
Скоуп: только то, что нужно для двух нод — (1) RANDOMIZER (проценты по веткам,
семантика распределения, фиксация/стикинг при повторном входе, выбор победителя);
(2) GOAL (целевое событие, окно атрибуции, exit-on-goal, атрибуция достижения на ветку
и конверсия по веткам). Каналы, триггеры, инспекторы прочих нод — вне ресёрча (см.
соседние RESEARCH-*.md).
Достоверность. Разделы Customer.io / Braze / Segment / Klaviyo основаны на first-party документации вендоров, каждый факт прошёл верификацию (в тексте помечены
подтверждено). Раздел Mindbox — по одной странице офиц. доков (help.mindbox.ru/docs/workflow-ab-tests), подтверждён фетчем и поиском, но узкий. HubSpot и Iterable в вопрос входили, но релевантных первичных фактов харнесс не вернул — считаем непокрытыми (см. §9).
Random-split — отдельная нода с процентами по N веткам; сумма всегда 100%. У всех
вендоров, где сплит есть как отдельная сущность (Customer.io «Random Cohort» — до 20
веток; Segment «Randomized Split» — до 5 веток), автор задаёт проценты вручную, и
продукт не даёт сохранить/опубликовать, пока сумма ≠ 100%. Это прямой референс для
нашей AutomationRandomizerNode (N динамических выходов с процентами, ADR-700 §…).
Распределение — статистическое, не точное. Каждый контакт раскидывается независимо и случайно; «50/50» даёт примерно, а не ровно поровну (явно оговорено у Klaviyo и выводится из Braze bucket-модели). В UI цель не «ровно N человек в ветку», а «доля».
Фиксация распределения при повторном входе — ключевая развилка, вендоры расходятся (§7.1). Три модели: (а) всегда стики — Mindbox (та же ветка при каждом повторном срабатывании); (б) стики условно — Segment v1 (та же ветка, только если у journey есть re-entry condition); (в) опция, по умолчанию ре-рандомизация — Segment v2 (тумблер «Assign same branch»). Customer.io добавляет 4-ю ось — стикинг на уровне объекта/когорты (все из одной компании идут одной веткой). Для нашей задачи («фиксация распределения контактов») нужно осознанно выбрать модель.
Цель = целевое событие + окно атрибуции; exit-on-goal — ОТДЕЛЬНАЯ настройка, а не
автоматика. Каноничный пример — Customer.io: достижение цели по умолчанию НЕ
выводит из сценария; чтобы выводило, критерий цели нужно продублировать в exit
conditions. У нас это уже заложено как флаг stopFlow на GOAL-ноде (ADR-500 §11) —
ресёрч подтверждает, что exit-on-goal должен быть явным переключателем, а не поведением
по умолчанию.
Окно атрибуции (conversion/attribution window) — обязательный параметр цели. Customer.io: настраиваемое, до 90 дней. Klaviyo: пер-канальные дефолты (email 5 дней, SMS-клик 5 дней / open 1 день, push 24 ч, WhatsApp 5 дней / 12 ч), кастомизация до 90 дней, окно стартует в момент отправки сообщения. Braze: рекомендует окно не меньше самой длинной ветки Canvas; фактически тянется на 3 дня сверх макс. длительности, минимум — 5 минут. У нашей GOAL-ноды окна атрибуции сейчас НЕТ — это пробел, который закрывает MLF-296.
Выбор победителя A/B — стат-значимость, а не «просто больше конверсий». Braze: Pearson chi-squared, ярлык «Winner» при p < 0.05 (95%); есть fallback «отправить лучший вариант всё равно», если порог не достигнут. Для MVP это, скорее всего, за рамками (у нас нет авто-winner), но важно как ориентир для будущей аналитики по веткам.
Атрибуция достижения цели именно на A/B-ветку — слабо задокументирована у всех. Явных механик «конверсия по конкретной ветке сплита» первичные доки почти не раскрывают (Customer.io лишь декларирует цель — «видеть, какая ветка даёт больше конверсий»). Это наш дифференциатор и одновременно открытый вопрос (§8): контракт мы, вероятно, проектируем сами, а не копируем.
Терминология: Random Cohort / Branch / Cohort by / Goal / conversion window / exit conditions.
Customer.io разделяет три типа flow-control нод:
Cohort by — единица случайного назначения: Profile (каждый сам по себе) либо
Object (напр. Company) — тогда все, связанные с одним объектом, идут одной
веткой (консистентный опыт для всей группы/аккаунта).
Источник: random-cohort +
release note 2025-11-06 (Random cohorts by object)."When someone reaches a random cohort block, we check their cohort. If the cohort has already been assigned to a branch, they'll go down that branch… future members of the same cohort will follow the same path." Т.е. повторный вход → та же ветка (стикинг per-cohort). Для
Profile-когорты это де-факто per-person стикинг. → Референс для нашей «фиксации распределения».
Терминология: Canvas / random bucket number / exit event / conversion window / multivariate test / Winner.
У Braze нет «процентной split-ноды» как в Customer.io; механика иная:
0–2499 / 2500–4999 /
5000–7499 / 7500–9999 = четыре равные группы по 25%.Терминология: Randomized Split / branch / re-entry condition / Assign same branch.
"Each branch receives a portion of the eligible users based on percentages that you assign to the branches." До 5 веток, процент на каждую; нельзя сохранить/опубликовать, пока сумма ≠ 100% и пока есть пустые. Назначение — случайное, по заданным долям. Источник: v1/step-types.
Терминология: conditional split / Random sample / YES-path / attribution window.
Дефолты (last-touch, окно стартует в момент отправки сообщения; кастомизируется до 90 дней):
| Канал / событие | Окно |
|---|---|
| Email клики / открытия | 5 дней |
| SMS клики | 5 дней |
| SMS открытия | 1 день |
| Push открытия | 24 часа |
| WhatsApp клики | 5 дней |
| WhatsApp открытия | 12 часов |
Конверсия (напр. placed order) атрибутируется сообщению, только если событие попало в открытое окно этого сообщения. Источники: message attribution, Klaviyo Academy — attribution.
A/B-тест в сценарии — стикинг по контакту:
«Если клиент попал в сценарий и был отнесён к одной из веток АБ-теста, то при повторном попадании в этот сценарий с АБ-тестом клиент будет отнесён к той же ветке. Если сценарий может отрабатывать несколько раз на клиенте, то клиент при каждом срабатывании будет попадать в одну и ту же ветку.» → Единственный вендор с безусловным per-person стикингом по умолчанию. Источник: help.mindbox.ru/docs/workflow-ab-tests.
| Вендор | Split как | Кол-во веток | Проценты | Стикинг при повторном входе | Окно атрибуции | Exit-on-goal |
|---|---|---|---|---|---|---|
| Customer.io | Random Cohort (отд. нода) | до 20 | вручную, Σ=100% | per-cohort (Profile/Object) | до 90 дн (настр.) | НЕ авто; через exit conditions |
| Braze | bucket 0–9999 + ручной сегмент | сколько диапазонов | вручную через диапазоны | персистентный bucket (де-факто стики) | ≥ длиннейшей ветки; +3 дня; min 5 мин | exit event (может = conversion) |
| Segment v1 | Randomized Split | до 5 | вручную, Σ=100% | стики, если есть re-entry condition | — (не в скоупе фактов) | — |
| Segment v2 | Randomized Split | до 5 | вручную, Σ=100% | по умолч. ре-рандом, опция «Assign same branch» | — | — |
| Klaviyo | conditional split + «Random sample» | YES/NO (можно цепочкой) | % на YES-путь | — (не в скоупе фактов) | пер-канальное (email 5д, SMS 5д/1д, push 24ч…) | — |
| Mindbox | A/B-тест в сценарии | (не уточнено) | (не уточнено) | безусловный per-person стикинг | — | — |
Прочерки — фактов харнесс по этой ячейке не вернул, а не «функции нет».
Задача MLF-296 требует «фиксацию распределения контактов». Варианты:
execution или отдельной таблице), опцию B — отложить. На согласование с М.Т.goal_reached/заказ), не привязана к каналу касания → пер-канальность плохо ложится.
Рекомендация: единое окно атрибуции на GOAL-ноде (число + единица: часы/дни,
максимум ~90 дн), стартует от входа контакта в сценарий (или от касания — уточнить, §8.5).
Это новое поле в data GOAL-ноды сверх текущей схемы (ADR-500 §11.2).Ресёрч подтверждает: exit-on-goal — явный переключатель, не дефолт. У нас уже есть
флаг stopFlow на GOAL (ADR-500 §11) — оставляем как есть, ресёрч валидирует решение.
Нюанс из Braze для рантайма: если контакт в PAUSE-шаге в момент достижения цели —
решить, выводим сразу или после окончания паузы (Braze досиживает Delay). Уточнить в §8.5.
// RANDOMIZER.data
{
"branches": [ // N ветвей
{ "id": "branch_0", "label": "A", "weight": 50 },
{ "id": "branch_1", "label": "B", "weight": 50 }
], // Σ weight === 100 (валидатор блокирует иначе)
"sticky": true // MVP: всегда true (per-person фиксация). Развилка §8.1
}
// GOAL.data — добавляется к текущим goalName/goalValue/currency/stopFlow
{
"attributionWindow": { "value": 7, "unit": "days" }, // НОВОЕ, развилка §8.3
"goalEvent": "order_completed" // целевое событие (уточнить связь с текущим goal_reached)
}
Атрибуция на ветку (метрика конверсии по веткам): при достижении цели контактом, чьё
execution прошло через RANDOMIZER, засечь его branch_id и инкрементить конверсию этой
ветки в cdp.automation_analytics_events. Механику первичные доки не раскрывают —
проектируем сами (§8.6).
Явной, задокументированной механики «атрибуция достижения цели на конкретную A/B-ветку + конверсия по веткам на нодах» ресёрч не нашёл ни у кого (Customer.io лишь декларирует цель, без контракта). Значит: (а) это реальный дифференциатор «умной платформы»; (б) риск — проектируем без референса, нужен собственный аккуратный дизайн атрибуции.
cdp-событие (заказ и т.п.) или
существующая GOAL-нода goal_reached? Нужно развести «цель как отдельное событие» и
«GOAL-нода как маркер в графе»./journeys/random-cohort/.Customer.io: branches · random-cohort · conversions Braze: random_bucket_numbers · multivariate_analytics · exit_criteria Segment / Twilio Engage: v1/step-types · v2/event-triggered-journeys-steps Klaviyo: message attribution · A/B test flow branches · Academy — attribution Mindbox: workflow-ab-tests
Макеты в Claude Design: (1) нода RANDOMIZER на холсте (N выходов с процентами + валидатор
Σ=100%) и её инспектор (ветки, веса, тумблер sticky); (2) конфигуратор GOAL (целевое
событие + окно атрибуции + stopFlow exit-on-goal) и отображение конверсии по веткам на
нодах. До апрува макетов М. Терентьевым в разработку НЕ берём (DoD карточки).