Ресёрч: 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).
- Два уровня — warning (не блокирует) и error (блокирует публикацию) — отраслевой
стандарт; ровно так уже разложено в нашем ADR-600 §8
(мягкая на autosave / жёсткая на publish). Ресёрч это подтверждает как правильную рамку.
- Лучший UX (Customer.io, Klaviyo, HubSpot-класс) — ошибка живёт на самой ноде
(бейдж/подсветка карточки) + отдельная панель-список ошибок, клик по строке →
фокус/зум на проблемную ноду, кнопка Publish заблокирована до исправления.
- Худший 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-версию НЕТ — вендоры делятся на два лагеря.
- Immutable + версии (наш путь по ADR-200 §5): Braze
Canvas («Update Canvas» → новая версия, история версий view-only, лимит 10 версий),
HubSpot (revision history, revert).
- Live-edit (правки прямо в опубликованном): Customer.io (нет отдельной immutable
версии, правка требует ручного Save).
Наш выбор immutable (правки → новый draft) — обоснован и совпадает с Braze/HubSpot.
- In-flight — тоже развилка, и вендоры расходятся. Наш ADR: доигрывают на своей
версии. Braze — МИГРИРУЕТ in-flight на новую версию. Customer.io — при
изменении delay пере-оценивает уже ждущих на месте. То есть «доигрывают на старой
версии» — наше осознанное решение, а не индустриальный дефолт; в макете это стоит
явно проговорить как гарантию, потому что у конкурентов бывает иначе.
- 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) и блокирует
активацию — воркфлоу остаётся inactive до исправления. Проверяются конфигурация нод,
креденшелы, структура связей. Источник: docs.n8n.io/workflows/publish,
PR #25195 («Validate workflows when publishing
from workflow history», смёржено фев-2026, релиз n8n 2.8.0).
- Но UX подачи ошибки — плохой: пользователю показывают только generic-сообщение
«Validation Failed» как тост/строку, без указания, какая нода сломана и какое
правило нарушено — некуда ткнуть, непонятно, что чинить. Issue про это закрыт как
«not planned». Источники: issue #12296 и др.
- Технический нюанс (для нас — предостережение): новая бэкенд-валидация в n8n
(
validateNodeCredentials) дублирует фронтовую (getNodeCredentialIssues) — ревьюер
в PR прямо отметил это как tech-debt. → У нас логика правил уже единая
(validate-graph.ts, ADR-600) и должна вызываться и инлайн на клиенте, и на publish
server-side — не плодить две реализации.
Вывод для нас: мало заблокировать publish (это n8n умеет) — надо показать, где именно
проблема. Красная точка на ноде из нашего макета — верное начало, но её недостаточно без
навигации к ноде.
2.2 Что уже заложено в нашем макете и чего не хватает
В текущем макете editor-ui-redesign.html состояния
ноды уже есть (раздел «Состояния ноды»):
- ошибка (блокирует) — красная рамка + красная точка-бейдж в углу карточки;
- не подключена (warning) — жёлтая пунктирная рамка + жёлтая точка.
Это соответствует и отраслевому стандарту «error/warning», и нашему hasError из
ADR-700 §5. Чего в макете пока нет и что надо добавить по
итогам ресёрча:
- Панель-список ошибок (отдельный блок в тулбаре/сайдбаре) со счётчиком «N ошибок» и
строками вида «Email „Спасибо за заказ“ — не заполнена тема».
- Клик по строке ошибки → фокус/зум/подсветка проблемной ноды на canvas — ключевой
недостающий приём (то, чего нет у n8n).
- Блокировка кнопки «Опубликовать», пока есть хотя бы одна error (не warning), с
пояснением почему.
- Решение: показывать 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-канваса:
- По hover на элемент на вкладке Flow показывается число контактов, дошедших до него,
и статус выполнения — прямо на ноде.
- Метрики зависят от типа ноды: Email — Delivered / Opens / Click-throughs с процентами;
SMS — sent / delivered / errors; Filter / Action / Condition — число запусков элемента.
- Drill-down: клик по элементу открывает сайдбар с настройками, статистикой и
списком контактов, для которых элемент сработал (вкладка «List of recipients»).
- Период: есть Filter-контрол выбора периода; данные — исторические агрегаты, не
live. Экспорт в CSV/XLS/XLSX (в т.ч. «только уникальные»).
- Источники: sendpulse.com/.../automation360/statistics,
.../view-email-statistics.
Для отстройки: мы уже уходим от «радуги» SendPulse в визуальном языке (ADR-700,
макет). Счётчики берём по смыслу (на ноде + drill-down), но подаём в нашем спокойном
cyan-стиле, а не как у них.
3.2 Customer.io — режим «Measure» поверх графа (подтверждено)
- В визуальном билдере есть режим «Measure»: метрики доставляемости накладываются на
сам workflow, видны в контексте графа.
- Цифры — внизу карточки каждой ноды; пользователь выбирает до 4 метрик + период на
ноду. Наведение на Opened/Clicked — сравнение human vs machine.
- «Path Totals» в начале каждой ветки — кумулятивные метрики по всей ветке (пер-бранч
агрегаты прямо на canvas).
- Данные — исторические агрегаты, атрибутированы к дате создания, период настраивается.
- Источник: docs.customer.io/journeys/metrics/campaign-metrics.
3.3 Klaviyo — тумблер «Show Analytics» + real-time поля (подтверждено; исходный claim скорректирован верификацией)
- В хедере flow-билдера есть иконка-тумблер «Show Analytics»: при включении карточки
sending-action разворачиваются прямо на канвасе и показывают embedded-аналитику
(open rate / click rate / conversion rate + период). (Первичный claim ресёрча ошибочно
утверждал, что у Klaviyo метрик на канвасе нет — верификация в 3 голоса это опровергла
по first-party доке; здесь приведён исправленный факт.)
- Клик по карточке сообщения → снапшот за 30 дней в левом сайдбаре (историческое).
- Real-time: у Klaviyo есть поля «Waiting» / «Needs review» — текущее число людей в
ноде (единственный достоверно найденный кейс «сейчас в ноде»).
- Drill-down до людей — вкладка «Recipient Activity».
- Источник: help.klaviyo.com/.../115002779351.
3.4 Braze — пер-компонентный funnel, но в report-view (подтверждено)
- Canvas показывает пер-шаговые метрики: сколько вошло, прошло дальше, вышло + impressions,
unique recipients, конверсии, выручка — step-level funnel.
- Но цифры не на нодах в редакторе, а в отдельной прокручиваемой панели на Canvas
Details (report-view); клик по компоненту → drill-down в канальную статистику.
- Источник: braze.com/.../measuring_and_testing_with_canvas_analytics.
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 (подтверждено):
- Новая версия создаётся кнопкой «Update Canvas» — правки идут в новую версию, а не
перезаписывают живую.
- История версий — отдельная фича; прошлые версии view-only (можно смотреть
аналитику и пути пользователей). Лимит 10 версий, версии переименовываются.
- Аналитика скоупится по версии: Canvas-level — за весь период, step-level — только для
шагов, существовавших в ту версию.
- Источники: braze.com/.../change_your_canvas_after_launch,
.../canvas_version_history.
HubSpot workflows (подтверждено):
- Полная хронологическая revision history (View → Revision History): что изменилось,
когда, какой пользователь; фильтр по дате/типу события/пользователю.
- Просмотр прошлой ревизии — read-only экран (триггеры/действия/настройки на тот момент,
без правки).
- Revert к прошлой ревизии загружает её в редактор; если воркфлоу активен — начинает
исполняться сразу, если не поставить на паузу. Revert недоступен для воркфлоу с
webhook/custom-code (только как справка).
- Источник: knowledge.hubspot.com/workflows/view-workflow-changes.
4.2 Лагерь B — live-edit (правка прямо в опубликованном)
Customer.io (подтверждено):
- Нет отдельной immutable-версии — можно править прямо в live/published кампании; правка
сообщения требует ручного Save (не autosave).
- In-flight пере-оценивается на месте: при сокращении delay уже ждущие, кто прождал
дольше нового значения, сразу идут дальше; при удлинении — ждут новый срок. Удаление
сообщения — ре-роутит ждущих на следующий шаг. Уже отправленные сообщения не пере-слать.
- Источник: docs.customer.io/journeys/workflow-changes.
4.3 In-flight при новой версии — вендоры расходятся
| Вендор |
Что происходит с людьми, уже идущими по сценарию |
| Наш ADR-200 |
Доигрывают на своей (старой) версии; dispatch читает graphSnapshot через publishedVersionId |
| Braze |
МИГРИРУЮТ на новую версию (и вошедшие после — тоже на новую) |
| Customer.io |
Пере-оцениваются на месте под новые настройки (live-edit-модель) |
Вывод: «доигрывают на старой версии» — наше осознанное решение ради предсказуемости
(prime-directive «не ломается»), а не то, что делают все. У Braze — ровно наоборот (миграция).
→ В макете этот инвариант стоит явно показать пользователю («люди, уже идущие по сценарию,
доиграют по прежней версии»), потому что интуиция из других систем может быть иной.
4.4 UI-паттерны lifecycle, которые берём
- Индикатор версии / история версий (Braze, HubSpot) — да.
- Read-only просмотр прошлой версии (Braze, HubSpot) — да; ложится на наш
readonly-режим
canvas (ADR-700 §7).
- Баннер «вы редактируете новый draft, опубликованная версия неизменна» при правке
живого — да (наш инвариант immutable требует это объяснить).
- Кнопки Publish / Pause / Archive с подтверждением — да; статус
paused уже в enum
(ADR-200). В текущем макете тулбар пока имеет только
«Опубликовать» / «Остановить» и статус «Опубликована» — надо дорастить до полного lifecycle.
- Diff между версиями — достоверно не нашёлся ни у кого из проверенных → не MVP.
- Лимит числа версий (Braze — 10) — на заметку, не обязательно для MVP.
5. Тема 4 · Re-entry policy (кратко)
Основной разбор re-entry — в MLF-286 (режимы «один раз /
каждый раз», cooldown Braze, наша reEntryPolicy из ADR-300 §7).
Здесь — только то новое, что дал этот ресёрч:
- Braze (подтверждено): re-entry (re-eligibility) — явная настройка на уровне
сценария («Canvas entry settings»), не отдельная нода. Конверсии считаются per-entry
(один и тот же человек, войдя дважды и сконвертировавшись оба раза, даёт 2 конверсии).
Нюанс на стыке с lifecycle: если re-entry разрешён, пользователи, уже прошедшие новые шаги,
становятся eligible войти снова.
- Вывод для UI: re-entry подаём на уровне сценария/старта, а не как ноду — совпадает
и с Braze, и с нашей моделью (
reEntryPolicy per-automation). Формулировки режимов «один
раз / каждый раз / раз в N дней» — из MLF-286, не переизобретаем.
6. Что берём в макеты (шаг 2) — сводный чек-лист
Валидация (canvas):
- [ ] Бейдж-состояние ошибки/warning на ноде — уже в макете, оставляем.
- [ ] Панель-список ошибок со счётчиком + строки с человекочитаемым текстом.
- [ ] Клик по ошибке → фокус/зум на ноду (ключевой приём, антипаттерн n8n — без этого).
- [ ] Publish заблокирован при наличии error (не warning) + пояснение.
- [ ] Единая функция правил (наш
validate-graph.ts) и на клиенте, и на publish — не
дублировать (урок n8n).
Счётчики (canvas):
- [ ] Цифры на карточке ноды (heatmap-оверлей / бейджи), разные наборы для channel- и
logic-нод.
- [ ] Явно развести исторические агрегаты (вошло/прошло/вышло за период) и «сейчас в
ноде» (real-time, дороже) — решить, что в MVP.
- [ ] Место под drill-down до списка людей (не-MVP).
- [ ] Селектор периода (и версии — позже).
Lifecycle:
- [ ] Полный тулбар: draft → Publish → Pause → Archive с подтверждениями.
- [ ] Баннер immutable («правки → новый draft; опубликованная версия неизменна»).
- [ ] Инвариант in-flight («уже идущие доиграют по своей версии») — показать явно.
- [ ] Индикатор версии + история версий (read-only просмотр через наш
readonly-режим).
- [ ] Diff версий — не MVP.
Re-entry:
- [ ] На уровне сценария/старта (не нода); режимы — из MLF-286.
7. Открытые вопросы к согласованию с М. Терентьевым
- Объём валидации в MVP. MLF-288 тянет из v1.1 (ADR-600 §8): reachability
(недостижимые ветки) и детект циклов без delay. Делаем их в MVP как error (блокирует),
как warning, или переносим на потом, а в MVP — только data-валидация (пустые поля,
обе ветки условия подключены)?
- «Сейчас в ноде» — делаем ли в MVP. Real-time-счётчик «сколько людей сейчас в ноде»
технически дороже исторических агрегатов (другой источник — активные executions, не
firehose-события). Достаточно ли для MVP исторических «вошло / прошло / вышло», а «сейчас» —
позже?
- Где показывать счётчики. On-canvas оверлей на нодах (SendPulse/Customer.io/Klaviyo-путь)
— или отдельная панель-report (Braze-путь), или оба? В ADR-700 заложен HeatmapOverlay
(v1.1) — подтверждаем это направление для MVP?
- Формулировка инварианта in-flight в UI. Как именно показать пользователю, что
правки не затронут уже идущих (текст баннера/подсказки), учитывая, что у части
конкурентов (Braze) поведение обратное — миграция?
- Полнота lifecycle-контролов в MVP. Нужны ли уже Archive и история версий с read-only
просмотром, или MVP ограничивается draft → publish → pause?
8. Достоверность и ограничения этого ресёрча
- Покрыто и верифицировано: SendPulse, Klaviyo, Customer.io, Braze, HubSpot, n8n — на
first-party источниках, adversarial-верификация в 3 голоса (68 подтверждено / 7 опровергнуто).
- Скорректировано верификацией: claim «у Klaviyo нет метрик на канвасе» — опровергнут
(есть тумблер «Show Analytics»); claim про «shared FE/BE функции валидации в n8n» —
опровергнут (на деле дублирование). В отчёте приведены исправленные факты.
- Не покрыто в этой итерации: Iterable, ActiveCampaign (ресёрч не набрал фактуры),
Mindbox (доступ к докам ограничен, как в MLF-286). Выводы по ним здесь не приводятся —
при необходимости отдельный догон перед макетами.
- Оговорка по методу: финальный агент-синтезатор workflow упал (StructuredOutput retry
cap); отчёт собран напрямую из верифицированных утверждений журнала, а не из
авто-синтеза — фактура та же, но формулировки/структура — авторские.
Приложение · источники (first-party)