Задача: MLF-293 — «Сценарии: симуляция/превью пути контакта + live-debugger». Подзадача зонтичной MLF-284 (июльский редактор сценариев). Это шаг 1 карточки (ресёрч); его результат ложится в шаг 2 (макеты превью пути и live-debugger — подсветка на canvas, панель стрима) и согласование с М. Терентьевым — без апрува в разработку не берём.
Дата: 2026-07-13
Метод: deep-research harness (6 углов → параллельные web-поиски → фетч 28 first-party
URL → извлечение 78 фалсифицируемых утверждений → adversarial-верификация в 3 голоса,
75 проверок: 67 подтверждено / 8 опровергнуто). Финальный агент-синтезатор упал на
StructuredOutput; отчёт собран напрямую из верифицированной фактуры журнала (не по памяти) —
как и в MLF-288.
Скоуп: строго две темы карточки — (1) симуляция / dry-run превью пути контакта
(выбор профиля, прогон по графу без реальных отправок, подсветка веток и объяснение «почему
пошёл сюда»); (2) live-debugger (лента контактов по нодам, фильтр по контакту, логи
исполнения из AutomationExecutions + cdp.automation_analytics_events). Наш контекст:
durable-движок automation (Flows/FlowVersions + AutomationExecutions, НЕ
Journey/Enrollment), @xyflow/react canvas, светлая тема, тулбар с Publish/валидацией/
версиями уже есть.
⚠️ Поправка по модели данных (ADR-000; исходный источник был неверным). В постановке MLF-293 и в первой редакции этого ресёрча debugger был привязан к
FlowExecutions/FlowExecutionLogs— но это таблицы Telegram Flow Builder (чат-боты), НЕ automation (admin.group: 'Flow Builder', связи наsenders/flows/flow-versions,sourceUpdateId). У automation отдельной таблицы логовAutomationExecutionLogsнет — и она не нужна. Для реализации задачи хватает того, что уже есть:
AutomationExecutions(public.automation_executions) — per-contact состояние:status(queued/running/waiting_delay/waiting_event/completed/error),currentNodeId,path: string[](последовательность пройденных нод),lastErrorMessage,startedAt. Этого достаточно для фильтра по контакту, подсветки пройденного пути и drill-down.cdp.automation_analytics_events— append-only лента событий шага (node_entered/node_executed/node_error/message_sent/message_skipped/flow_completed/…) сperson_id,node_id,node_type,created_at— для хронологической ленты debugger'а.Ниже по тексту
FlowExecutionLogsзаменён на эти сущности.
Достоверность и покрытие. Верифицированная 3-голосовая фактура набрана по четырём вендорам: Braze, HubSpot, Customer.io, n8n — на first-party документации (help-центры, docs). Дополнительно верифицирован Adobe Journey Optimizer (вне исходного списка, но сильный первоисточник с важной для нас развилкой). SendPulse — 10 утверждений сняты прямо с его first-party доков (Automation360), но через 3-голосовую верификацию не прогонялись; все они — про отсутствие фичи, потому риск низкий, помечаю отдельно. По Klaviyo, Iterable, ActiveCampaign эта итерация фактуры не набрала (URL фетчились, но утверждения не прошли извлечение/верификацию) — их выводов здесь нет, не выдумываем; при необходимости — отдельный догон. Mindbox в выборку не попал (как и в прошлых ресёрчах — доступ к докам ограничен).
Dry-run превью пути — зрелый отраслевой стандарт, эталон = Braze «Preview User Paths».
Кнопка Test Canvas в футере билдера → выбор тестового пользователя → Run Test.
Прогон без реальных отправок (Liquid обрабатывается, но сообщения не уходят, вебхуки
не срабатывают, User Update не пишется в реальный профиль). Показывает тайминги, какие
сообщения/каналы ушли бы, и — ключевое — на branch-шагах выводит met/unmet criteria,
т.е. объясняет «почему пошёл в эту ветку» (подтверждено, 8 вердиктов high). Это ровно
то, что просит MLF-293 — берём как референс №1.
Подсветка предсказанного пути на canvas — тоже стандарт, и есть два визуальных языка:
«Почему пошёл сюда» = показать сработавшее условие + модель first-match. Индустрия объясняет ветвление одинаково: контакт идёт по ПЕРВОЙ ветке, чью условие выполнил (Braze Audience Paths «put the most important path first»; HubSpot «follows the first branch condition it meets»; Customer.io «evaluate criteria to determine which path») (подтверждено у всех трёх). В превью надо на branch-ноде показать: какие критерии выполнены/не выполнены и почему выбрана именно эта ветка.
Детерминизм достигается фиксацией входных данных. n8n даёт это через pin data (заморозка вывода ноды) и mock-датасеты — «iterate without operating on variable data», прод игнорирует pinned data (подтверждено). Для нашего критерия приёмки «unit на детерминизм» модель та же: симуляция оценивает предикаты веток против зафиксированного снапшота профиля, first-match, без побочных эффектов.
Выбор субъекта прогона: реальный ИЛИ тестовый профиль — да, но «тестовый» ≠ синтетика. HubSpot гоняет по конкретной существующей записи (поиск в дропдауне). У Braze «test user» — это реальный профиль, назначенный для тестов (а не фейк-аккаунт): claim, что Braze принимает «и реальные, и выделенные тестовые» как две категории, опровергнут — в доках только «select a test user» (3 вердикта REFUTED). Вывод: делаем выбор любого реального профиля как субъекта dry-run; отдельная сущность «тестовый профиль» индустрией не навязывается.
⚠️ Anti-pattern (Adobe): «test mode» ≠ «dry run». У AJO Test mode РЕАЛЬНО ШЛЁТ сообщения (генерит реальное событие, задевает другие journey) — это НЕ прогон без отправок; no-send-симуляция у Adobe называется отдельным «Dry run» (3 claim'а про «test mode без отправок» ОПРОВЕРГНУТЫ). Урок для нас: симуляция обязана быть жёстко без сайд-эффектов (ни отправок, ни записи событий, ни мутации профиля) — иначе повторяем грабли Adobe.
Главный вывод: «живого real-time стрима контактов по нодам» в индустрии практически
НЕТ. Отраслевой паттерн — пост-фактум просмотр логов исполнения по контакту, а не
стрим в реальном времени. Ни у Braze, ни у HubSpot, ни у Customer.io, ни у n8n
верифицированно не нашлось живого стрима контактов, текущих через ноды. Это значит:
пункт MLF-293 «стрим в реальном времени» — амбициознее рынка; реалистичный MVP —
near-real-time лента событий из cdp.automation_analytics_events (poll/refresh), а не
настоящий streaming. Об этом — открытый вопрос №3 ниже.
Отраслевой стандарт debugger = per-contact execution log с drill-down. Три реализации, все (подтверждены):
Фильтр по контакту — универсальный must-have debugger'а. HubSpot (поиск записи в
enrollment history), Customer.io (профиль клиента + Identified/Anonymous), n8n (custom-data
filter) — все дают сузить лог до одного субъекта (подтверждено). У нас это ложится прямо
на AutomationExecutions (состояние/путь) + cdp.automation_analytics_events (события) со
скоупом по контакту.
Замыкание debugger'а на «почему»: та же first-match модель, что и в симуляции. HubSpot в enrollment history подсвечивает пройденные ветки и endpoint для реальной записи — т.е. тот же UX-язык объяснения ветвления работает и для симуляции (предсказание), и для debugger'а (факт). Это позволяет нам сделать один визуальный слой объяснения для обеих фич.
Проверено по editor-ui-redesign.html (текущее состояние UI после 5 ресёрчей):
@xyflow/react, cyan-акцент (#02CBEF): выделенная нода — cyan-ring+glow,
выбранное ребро — cyan. Значит для симуляции нужен отдельный визуальный код (зелёный
«пройденный путь» + приглушение непройденных веток), чтобы не конфликтовать с выделением.То есть будущие макеты MLF-293 пристраивают к этому макету две новые поверхности: (а) режим симуляции поверх canvas + панель объяснения; (б) панель debugger'а (лента + фильтр по контакту).
Точка входа — кнопка Test Canvas в футере билдера Canvas → Select a test user → Run Test. Свойства прогона:
User Update НЕ пишется в реальный профиль.*Важный нюанс (подтверждено):* сам компонент Audience Paths на canvas не объясняет inline, почему юзер попал в ветку, и не показывает per-branch счётчики — это выведено именно в превью-прогоне, а метрики живут отдельно в аналитике. У Braze есть и второй метод теста — дубль Canvas с ограничением входной аудитории до тест-юзеров и фильтром по email; это реальный запуск с реальными (тестовыми) отправками, не dry-run (подтверждено; claim, что «подход Braze — это НЕ чистый dry-run», ОПРОВЕРГНУТ — Braze ships именно dry-run как метод №2).
Берём: модель Braze один-в-один как референс режима симуляции — вход из тулбара, выбор профиля, прогон без сайд-эффектов, объяснение met/unmet на branch-нодах, конфигурируемое время входа (позже — не MVP).
Берём: зелёную подсветку пройденного пути на canvas + first-match объяснение как основной визуальный язык симуляции (совпадает с Braze по семантике, добавляет наглядную подсветку).
JOtestmode-*). No-send-симуляция у Adobe — это отдельная фича «Dry run».Урок: терминологию «test mode» индустрия использует и для реальных тестовых отправок. Наш режим — это именно dry-run без сайд-эффектов; в UI стоит назвать его так, чтобы не путали с «отправить тестовое письмо» (у нас будущая отдельная кнопка). Приём Adobe «переставь ветки, чтобы протестить другой путь» — анти-паттерн относительно того, что просит MLF-293: мы, наоборот, хотим оценивать реальные условия против выбранного профиля и показывать сработавшее — это ближе к Braze/HubSpot, а не к AJO-split.
n8n технически ближе всех к нашему durable-движку, поэтому берём его механику детерминизма:
Берём (в модель, не в UI): для критерия приёмки «unit на детерминизм симуляции» — оценка веток против зафиксированного снапшота профиля (аналог pinned data), first-match, без внешних вызовов. «Partial run от ноды» — хорошая идея на будущее, не MVP.
Прямой конкурент для отстройки. По его же докам Automation360 (statistics, condition, filter, view-automated-flows):
Формулировки — «отсутствие фичи» из первичных доков; риск ошибки низкий, но помечаю, что эти пункты не проходили 3-голосовую верификацию (в отличие от Braze/HubSpot/CIO/n8n).
Что это даёт нам: симуляция + объяснение ветвления + подсветка на canvas — прямая отстройка от SendPulse. У них — только агрегаты постфактум; у нас — предсказание пути «до» и объяснение «почему». Это сильный дифференциатор, ровно в духе project-memory про отстройку.
| Аспект | Отраслевой стандарт | Развилка / решение для нас |
|---|---|---|
| Прогон без отправок | Да (Braze, HubSpot) | Жёстко без сайд-эффектов (урок Adobe) |
| Подсветка пути на canvas | Зелёный (HubSpot, Adobe) | Зелёный «пройдено» + приглушение непройденного, поверх нашей cyan-канвы |
| «Почему пошёл сюда» | met/unmet criteria + first-match (Braze, HubSpot, CIO) | Показать сработавшее условие на branch-ноде |
| Субъект прогона | Реальная запись (HubSpot) / test user = реальный профиль (Braze) | Выбор любого реального профиля; отдельная сущность «тестовый» не обязательна |
| Детерминизм | pin/mock данных (n8n) | Снапшот профиля + first-match, без внешних вызовов |
| Конфиг времени входа | Есть (Braze past/future) | Не MVP, заложить место |
Верифицированно не нашлось ни одного вендора с настоящим real-time стримом контактов, текущих через ноды на canvas. Индустриальный паттерн — исторический, пост-фактум лог исполнения с фильтром по контакту:
Следствие для MLF-293. Формулировка карточки «стрим контактов в реальном времени» —
амбициознее рынка. Реалистичный MVP поверх нашей cdp.automation_analytics_events —
near-real-time лента (периодический poll/refresh новых событий), а не настоящий streaming
через WS. Это — открытый вопрос №3 к согласованию.
Берём: статус-фильтр (Running/Success/Failed/Waiting) как модель фильтрации ленты исполнения; идею «открыть прошлый прогон контакта и посмотреть, что было» — как drill-down.
Берём: это прямой референс нашего debugger'а. Фильтр по контакту → панель истории с
таймстемпами и статусами шагов + подсветка пройденного пути на canvas — один-в-один ложится
на AutomationExecutions (состояние/путь) + cdp.automation_analytics_events (события).
Красный на ошибках — переиспользуем язык из
guardrails-ресёрча.
Берём: типизированный фильтр событий (по статусу/типу — доставка, ошибка, пропуск) для
нашей ленты; предупреждение — окно ретенции (у CIO 30 дней, у HubSpot 6 мес): для нашего
debugger'а над cdp.automation_analytics_events надо решить окно/пагинацию (открытый вопрос №4).
AutomationExecutions + cdp.automation_analytics_events)AutomationExecutions (состояние/путь контакта) + ленты
cdp.automation_analytics_events (события шага) со скоупом по workspace
(tenancy) и фильтром по контакту (person_id) — прямой
аналог HubSpot enrollment-history / CIO per-customer. Отдельный AutomationExecutionLogs не нужен.node_entered/node_executed/node_error/message_skipped…) и
таймстемпами (created_at) — как Activity Type у CIO.Режим «Симуляция» (dry-run):
Live-debugger:
cdp.automation_analytics_events (нода, событие, таймстемп, статус;
ошибки красным); состояние/путь контакта — из AutomationExecutions.Общий визуальный слой: один механизм «подсветка пути + объяснение ветвления» на две фичи (предсказание в симуляции, факт в debugger'е) — как у HubSpot.
Проговорено с М. Терентьевым; эти решения — бриф для макетов (шаг 2):
cdp.automation_analytics_events (напр. refresh каждые 3–5с), НЕ настоящий WS/SSE-streaming. Совпадает с
рынком (живого стрима по нодам ни у кого нет), укладывается в ~12ч, критерий «smoke на
стрим» проходит.Проработаны поверх реальной сетки макета editor-ui-redesign.html: слева тёмный app-сайдбар (256px) + палитра-рельс (56→190px) + canvas + правый инспектор ноды 420px, сверху тулбар (Publish/версии/валидация).
A. Панель debugger'а — нижний dock (не правый drawer). Правый борт уже занят инспектором 420px; лента исполнения — горизонтально-табличные данные (время · контакт · нода · событие · статус), которым нужна ширина. Нижний dock даёт ментальную модель «консоль/лог» (как n8n Executions) и естественную вертикальную пару: путь контакта подсвечен на canvas ↑, его лог скроллится снизу ↓. Панель collapsible/resizable по высоте, по умолчанию свёрнута (не ест высоту canvas).
B. «Симуляция» — отдельный read-only режим (не transient-оверлей на редактируемой канве). Симуляция по природе read-only (во время прогона не редактируешь). Смешивать edit-аффордансы (drag, cyan-выделение) с результат-подсветкой — визуальная каша. Braze/HubSpot ровно так: ты входишь в прогон (Test Canvas / Test) — граф тот же, но это отдельный акт. В режиме тулбар меняется на контролы симуляции (выбор профиля → Run → повтор → выход), edit-аффордансы приглушены.
C. Симуляция и debugger — два входа, один общий движок отрисовки.
AutomationExecutions (+ лента событий cdp.automation_analytics_events), нижний dock.
Оба рисуют один и тот же слой «зелёный путь + почему на branch-ноде» (решение §6.5) — движок
строим один раз. Разделяем входы, т.к. это разные стадии жизненного цикла (design-time QA vs
run-time troubleshooting) с разным контекстом тулбара (draft vs active по
guardrails-ресёрчу); общий визуальный язык
маркетолог учит один раз.D. Палитра прогона (разводим с брендовым cyan). Зелёный = пройденная нода/ребро; серый/приглушённый = не пройдено; красный = ошибка/упал бы (переиспользуем язык guardrails). Cyan-кольцо выделения в sim/debug-режиме подавляем (или тонкий контур), чтобы зелёный доминировал — иначе зелёная подсветка и cyan-выделение спорят на одной ноде.
E. «Почему» — бейдж на branch-ноде + деталь в инспекторе. Компактный бейдж на branch-ноде (какая ветка выбрана: «✓ открыл письмо» / «✗ не выполнил») + клик → инспектор с met/unmet деталью. Canvas остаётся читаемым, детали — по требованию (паттерн Braze/HubSpot: сам компонент ветвления не грузит inline-объяснением).
Итог для макетов: отдельный read-only режим «Симуляция» на черновике + нижний dock-debugger на активном flow, оба на общем движке подсветки (зелёный путь + бейдж «почему»), cyan-выделение уступает зелёному в режимах прогона.
Braze
HubSpot
Customer.io
n8n
Adobe Journey Optimizer (вне исходного списка, но верифицированный первоисточник)
SendPulse (first-party, без 3-голосовой верификации — «отсутствие фичи»)
Не набралось фактуры (для возможного догона): Klaviyo (help.klaviyo.com/hc/en-us/articles/115002779471, .../115002779351), Iterable (support.iterable.com/hc/en-us/articles/10044063369492, .../5367225442324), ActiveCampaign, Mindbox.