Ресёрч: guardrails публикации, валидация графа, счётчики на нодах и lifecycle у конкурентов (MLF-288, шаг 1)

Задача: MLF-288 — «Сценарии: guardrails публикации + счётчики на нодах». Это шаг 1 карточки (ресёрч); его результат ложится в шаг 2 (макеты ошибок валидации на canvas, lifecycle-контролов, счётчиков на нодах) и согласование с М. Терентьевым — без апрува в разработку не берём.

Дата: 2026-07-13 Метод: deep-research harness (5 углов → параллельные web-поиски → фетч ~15 first-party источников → извлечение 59 фалсифицируемых утверждений → adversarial-верификация в 3 голоса, 75 проверок: 68 подтверждено / 7 опровергнуто). Финальный агент-синтезатор упал на StructuredOutput; отчёт собран напрямую из верифицированной фактуры журнала (не по памяти). Скоуп: четыре guardrail-темы карточки — (1) pre-publish валидация графа и UX ошибки на canvas, (2) счётчики/аналитика на нодах, (3) lifecycle публикации и версионирование, (4) re-entry policy (кратко, со ссылкой на MLF-286). Сами правила валидации (что проверять) уже зафиксированы в ADR-600 §8 — ресёрч про UX подачи, а не про переизобретение правил.

Достоверность и покрытие. Верифицированная фактура собрана по шести вендорам: SendPulse, Klaviyo, Customer.io, Braze, HubSpot, n8n — на first-party документации (help-центры, docs, GitHub-коммиты/PR) с прошедшей adversarial-верификацией. По Iterable, ActiveCampaign эта итерация ресёрча фактуры не набрала — их выводы здесь не приводятся (не выдумываем; при необходимости — отдельный догон). Mindbox — как и в MLF-286, доступ к докам ограничен, в выборку не попал. Раздел по каждой теме помечает, где вывод — отраслевой стандарт, а где — расхождение вендоров.


1. TL;DR — что берём в макеты

Тема 1 · Валидация графа (UX ошибки на canvas).

  1. Два уровня — warning (не блокирует) и error (блокирует публикацию) — отраслевой стандарт; ровно так уже разложено в нашем ADR-600 §8 (мягкая на autosave / жёсткая на publish). Ресёрч это подтверждает как правильную рамку.
  2. Лучший UX (Customer.io, Klaviyo, HubSpot-класс) — ошибка живёт на самой ноде (бейдж/подсветка карточки) + отдельная панель-список ошибок, клик по строке → фокус/зум на проблемную ноду, кнопка Publish заблокирована до исправления.
  3. Худший UX (n8n) — антипаттерн, которого избегаем: валидация только по клику «активировать», generic-тост «Validation Failed» без указания, какая нода сломана. Пользователю некуда ткнуть. Наш макет уже закладывает верный путь (красная точка на ноде) — надо добавить панель ошибок с кликом-к-ноде.

Тема 2 · Счётчики на нодах. 4. On-canvas счётчики — реальный отраслевой приём, не экзотика. Его делают минимум трое: SendPulse (цифры на ноде по hover + drill-down в список контактов), Customer.io (режим «Measure» — до 4 метрик + период прямо на карточке ноды), Klaviyo (тумблер «Show Analytics» разворачивает карточки на канвасе). Это подтверждает, что счётчики на ноде из MLF-288 — валидное направление, а не хотелка. 5. Ключевая развилка (§ниже) — «живое» vs «историческое». Почти вся индустрия показывает исторические агрегаты за период (SendPulse, Customer.io, Klaviyo-снапшот 30 дней, Braze). Real-time «сейчас в ноде» нашёлся достоверно только у Klaviyo (поля «Waiting» / «Needs review»). MLF-288 хочет «сейчас находится» — это отдельная, более дорогая метрика, её надо отделить от исторических. 6. Drill-down «клик по числу → список людей» — есть у SendPulse, Klaviyo, Braze. Мощная, но не-MVP фича; заложить место.

Тема 3 · Lifecycle публикации. 7. Единого отраслевого ответа на immutable-версию НЕТ — вендоры делятся на два лагеря.

  1. In-flight — тоже развилка, и вендоры расходятся. Наш ADR: доигрывают на своей версии. Braze — МИГРИРУЕТ in-flight на новую версию. Customer.io — при изменении delay пере-оценивает уже ждущих на месте. То есть «доигрывают на старой версии» — наше осознанное решение, а не индустриальный дефолт; в макете это стоит явно проговорить как гарантию, потому что у конкурентов бывает иначе.
  2. UI-паттерны версионирования, которые берём: индикатор/история версий (Braze, HubSpot), read-only просмотр прошлой версии (HubSpot, Braze), баннер/подтверждение при правке живого. Diff между версиями достоверно не нашёлся ни у кого — не MVP.

Тема 4 · Re-entry policy. 10. Подтверждено достоверно только у Braze — настраивается в entry settings (уровень сценария, не отдельной ноды), конверсии считаются per-entry. Остальное (режимы «один раз / каждый раз / раз в N дней») уже разобрано в MLF-286 и лежит на нашей reEntryPolicy (ADR-300 §7) — здесь не дублируем.


2. Тема 1 · Pre-publish валидация графа и UX ошибки на canvas

Что нам нужно от этой темы (не путать с правилами). Список правил валидации у нас уже есть — ADR-600 §8 задаёт коды ошибок и делит на мягкую (autosave, предупреждения) и жёсткую (publish, блокирует). Ресёрч отвечает на другой вопрос: как показать эти ошибки пользователю на canvas, чтобы битый граф нельзя было запустить, а исправить было очевидно куда.

2.1 Диапазон качества UX — от эталона до антипаттерна

n8n — антипаттерн, который прямо избегаем. (подтверждено)

Вывод для нас: мало заблокировать publish (это n8n умеет) — надо показать, где именно проблема. Красная точка на ноде из нашего макета — верное начало, но её недостаточно без навигации к ноде.

2.2 Что уже заложено в нашем макете и чего не хватает

В текущем макете editor-ui-redesign.html состояния ноды уже есть (раздел «Состояния ноды»):

Это соответствует и отраслевому стандарту «error/warning», и нашему hasError из ADR-700 §5. Чего в макете пока нет и что надо добавить по итогам ресёрча:

  1. Панель-список ошибок (отдельный блок в тулбаре/сайдбаре) со счётчиком «N ошибок» и строками вида «Email „Спасибо за заказ“ — не заполнена тема».
  2. Клик по строке ошибки → фокус/зум/подсветка проблемной ноды на canvas — ключевой недостающий приём (то, чего нет у n8n).
  3. Блокировка кнопки «Опубликовать», пока есть хотя бы одна error (не warning), с пояснением почему.
  4. Решение: показывать error инлайн во время редактирования (как только поле опустело) или только по клику Publish. Отраслевой тренд — инлайн; для MVP допустимо по Publish, но бейдж на ноде — сразу.

2.3 Расхождение с ADR-600: MLF-288 вытягивает часть правил из v1.1 в MVP

Важно для согласования. Правила, которые MLF-288 называет в критериях приёмки — «нет недостижимых веток», «у каждого if обе ветки/default», «нет бесконечных циклов без delay» — в ADR-600 §8 помечены как Post-MVP / v1.1 (таблица «Правила Post-MVP»). То есть MLF-288 фактически переносит эти проверки в MVP. Это не противоречие, а осознанный сдвиг объёма — но его надо явно проговорить с Михаилом на согласовании макетов: делаем ли мы reachability-анализ и детект циклов уже сейчас, или в MVP ограничиваемся data-валидацией (пустые поля, обе ветки подключены), а reachability/циклы — как warning. Ресёрч тут не решает за продукт — только фиксирует развилку.


3. Тема 2 · Счётчики / аналитика на нодах

Главный вывод: показывать метрики прямо на карточке ноды на canvas — это реальный отраслевой приём, а не наша самодеятельность. Ниже — кто как это делает, чтобы обосновать макет и выбрать модель.

3.1 SendPulse (Automation360) — визуальный ориентир отстройки (подтверждено)

Самый близкий к нам кейс — у SendPulse счётчики живут на нодах flow-канваса:

Для отстройки: мы уже уходим от «радуги» SendPulse в визуальном языке (ADR-700, макет). Счётчики берём по смыслу (на ноде + drill-down), но подаём в нашем спокойном cyan-стиле, а не как у них.

3.2 Customer.io — режим «Measure» поверх графа (подтверждено)

3.3 Klaviyo — тумблер «Show Analytics» + real-time поля (подтверждено; исходный claim скорректирован верификацией)

3.4 Braze — пер-компонентный funnel, но в report-view (подтверждено)

3.5 Klaviyo (reporting) — контрпример «аналитика отдельно» (подтверждено)

Отдельные flow-performance отчёты Klaviyo (recipients / opens / clicks / conversions) живут в отдельной таблице-отчёте, не на канвасе, — историческое, не auto-refresh, с выбором периода и гранулярности (день/неделя/месяц). То есть даже у одного вендора уживаются два представления: on-canvas (быстрый взгляд) и report (глубокий разбор).

3.6 Свод по теме 2 — что берём и где развилки

Аспект Отраслевой стандарт Наше решение / развилка
Где цифры На ноде (SendPulse, Customer.io, Klaviyo) ИЛИ в report-панели (Braze) Наш ADR-700 §7 уже закладывает HeatmapOverlay поверх canvas (v1.1) + статистику в инспекторе (вошло/вышло/конверсия). Совпадает с on-canvas-лагерем.
Живое vs историческое Почти все — исторические агрегаты за период; real-time редок (только Klaviyo Waiting/Needs review) MLF-288 просит «сейчас находится» — это отдельная real-time метрика. Развилка: делаем ли «сейчас» в MVP или только исторические вошло/прошло/вышло. Наши firehose-таблицы cdp.*_analytics_events (append-only события) естественно дают исторические агрегаты; «сейчас в ноде» считается иначе (по активным executions), это другой источник.
Метрики по типу ноды Email — opens/clicks; логика — число запусков (SendPulse) Учесть: на channel-нодах и на logic-нодах показываем разные наборы.
Drill-down до людей Есть у SendPulse, Klaviyo, Braze Заложить место; не-MVP.
Период / версия Выбор периода — везде; выбор версии — у Braze (per-version scope) Наш селектор периода уже в тулбаре (ADR-700 §7.3, v1.1).

Про источник данных (заземление). ADR-200 §7: у нас две firehose-таблицы — cdp.flow_analytics_events (Telegram Flow) и cdp.automation_analytics_events (Automation), append-only, с per-node event_type. Именно из них MLF-288 просит считать «вошло / прошло / вышло». Но per-node UI-счётчики в ADR помечены v1.1 (§7.3 — индекс (flow_version_id, node_id, event_type) в MVP не создаётся) — как и HeatmapOverlay (ADR-700). То есть, как и с валидацией, MLF-288 вытягивает счётчики на нодах из v1.1 в MVP — вторая точка согласования по объёму.


4. Тема 3 · Lifecycle публикации и версионирование

Здесь нет единого отраслевого ответа — вендоры делятся на два лагеря, и наш выбор (immutable опубликованная версия, правки → новый draft, in-flight доигрывают на своей версии, ADR-200 §5) совпадает с одним из них, а не с «индустриальным дефолтом». Это важно понимать при согласовании: мы выбираем сторону.

4.1 Лагерь A — immutable + версии (наш путь)

Braze Canvas (подтверждено):

HubSpot workflows (подтверждено):

4.2 Лагерь B — live-edit (правка прямо в опубликованном)

Customer.io (подтверждено):

4.3 In-flight при новой версии — вендоры расходятся

Вендор Что происходит с людьми, уже идущими по сценарию
Наш ADR-200 Доигрывают на своей (старой) версии; dispatch читает graphSnapshot через publishedVersionId
Braze МИГРИРУЮТ на новую версию (и вошедшие после — тоже на новую)
Customer.io Пере-оцениваются на месте под новые настройки (live-edit-модель)

Вывод: «доигрывают на старой версии» — наше осознанное решение ради предсказуемости (prime-directive «не ломается»), а не то, что делают все. У Braze — ровно наоборот (миграция). → В макете этот инвариант стоит явно показать пользователю («люди, уже идущие по сценарию, доиграют по прежней версии»), потому что интуиция из других систем может быть иной.

4.4 UI-паттерны lifecycle, которые берём


5. Тема 4 · Re-entry policy (кратко)

Основной разбор re-entry — в MLF-286 (режимы «один раз / каждый раз», cooldown Braze, наша reEntryPolicy из ADR-300 §7). Здесь — только то новое, что дал этот ресёрч:


6. Что берём в макеты (шаг 2) — сводный чек-лист

Валидация (canvas):

Счётчики (canvas):

Lifecycle:

Re-entry:


7. Открытые вопросы к согласованию с М. Терентьевым

  1. Объём валидации в MVP. MLF-288 тянет из v1.1 (ADR-600 §8): reachability (недостижимые ветки) и детект циклов без delay. Делаем их в MVP как error (блокирует), как warning, или переносим на потом, а в MVP — только data-валидация (пустые поля, обе ветки условия подключены)?
  2. «Сейчас в ноде» — делаем ли в MVP. Real-time-счётчик «сколько людей сейчас в ноде» технически дороже исторических агрегатов (другой источник — активные executions, не firehose-события). Достаточно ли для MVP исторических «вошло / прошло / вышло», а «сейчас» — позже?
  3. Где показывать счётчики. On-canvas оверлей на нодах (SendPulse/Customer.io/Klaviyo-путь) — или отдельная панель-report (Braze-путь), или оба? В ADR-700 заложен HeatmapOverlay (v1.1) — подтверждаем это направление для MVP?
  4. Формулировка инварианта in-flight в UI. Как именно показать пользователю, что правки не затронут уже идущих (текст баннера/подсказки), учитывая, что у части конкурентов (Braze) поведение обратное — миграция?
  5. Полнота lifecycle-контролов в MVP. Нужны ли уже Archive и история версий с read-only просмотром, или MVP ограничивается draft → publish → pause?

8. Достоверность и ограничения этого ресёрча


Приложение · источники (first-party)