# ADR-0004: Steady-stream источник — синтетический иерархический генератор, не реплей Принято: 2026-06-09 Статус: accepted Связано: [`generator/KNOWN_ISSUES.md`](../../generator/KNOWN_ISSUES.md) (диагноз дефекта), [`CONTEXT.md`](../../CONTEXT.md), [ADR-0002](./0002-specs-as-durable-design-docs.md) (спеки как durable design-доки), спека [`docs/specs/2026-06-09-generator-rework-hierarchical.md`](../specs/2026-06-09-generator-rework-hierarchical.md) (форма доработки). ## Решение Режим `steady-stream` (живой поток на стенде) питается **синтетическим генератором, переписанным с нуля по иерархической модели** «популяция пользователей → сессии → события», а **не реплеем статического сида**. Модель интенсивности текущего генератора (Poisson по тикам + дневной коэффициент + jitter) сохраняется. Режим `bootstrap` (статический сид `data/*.jsonl`) и уроки 0–6 остаются прежними — генератор **сосуществует** с сидом, не заменяет его. ## Контекст Проект сменил назначение на учебный стенд с курсом. Базовый курс стоит на статическом сиде, и это осознанно: сид **честен внутри визита** (`click_id` группирует несколько событий — настоящая воронка; по полному замеру 2026-06-10 — 1..27 событий, медиана 10, см. профиль сид-датасета в `CONTEXT.md`). Но у сида два потолка, которые сид принципиально не закрывает: - **`users == sessions`** — каждый пользователь имеет ровно один `click_id` (1:1), возвращающихся пользователей нет (см. `CONTEXT.md`). - **одноразовость** — сид заливается батчем; не видно, как ClickHouse и витрины ведут себя на непрерывном живом потоке. Нужен режим, в котором стенд **живёт и движется** на правдоподобных данных (воронка + жизнеподобные колебания интенсивности), при **скромном объёме** (у менти может не быть мощного железа — на объём/стресс не закладываемся). Двойная учебная ценность — ключевой критерий выбора: ценны не только *данные* (живой стенд + честная пирамида `users < sessions < events`), но и **сам генератор как объект изучения** — его генеративная модель достойна того, чтобы её разбирать в курсе. Текущая реализация генератора не дорабатывается инкрементально: у неё концептуальный дефект модели сущностей (свежий `click_id` на каждое событие схлопывает иерархию — см. `generator/KNOWN_ISSUES.md`), переписываем с нуля. ## Рассмотренные варианты - **A — синтетический иерархический генератор (принято).** Популяция юзеров с постоянным `user_domain_id` → 1..N сессий (`click_id` на сессию) → 1..M упорядоченных по времени событий с правдоподобной воронкой. Единственный вариант, дающий возвращающихся пользователей (`users < sessions < events`) и учебную ценность самого моделирования. Цена — самый большой объём работы и риск ошибиться в статистической модели. - **C — реплей честного сида на часах.** Лить реальные записи сида в Kafka во времени, переписывая `event_timestamp` в «сейчас», зацикливая пул и модулируя rate. **Отклонено.** Дёшев и даёт гарантированно честную воронку почти без риска, **но**: (1) обходит ровно ту генеративно-модельную часть, ради учебной ценности которой всё и затевается; (2) наследует вырождение `users == sessions` из сида — полную пирамиду не даёт никогда; (3) «бесконечность» = зацикленный конечный пул. - **B — минимальный «живой» генератор с грубой воронкой.** Тот же rewrite, но воронка на фиксированных вероятностях, без глубины. Отклонено как половинчатое: числа воронки менее убедительны, а вопрос возвратов всё равно надо решать — то есть основной сложности не избегает. ## Последствия - Режим `steady-stream` фиксируется как синтетическая генерация; направление переоткрывать не нужно (типовой вопрос «почему не реплей?» закрыт здесь). - `bootstrap`-сид и уроки 0–6 не трогаем; живой поток подаётся отдельным уроком 7. - Контракт данных потока: здоровая пирамида `users < sessions < events`, монотонное время внутри сессии, `click_id` переиспользуется внутри сессии. Этот контракт наследуют будущие артефакты (спека доработки, урок 7, возможные правки витрин). - Детальная архитектура/требования — в спеке `2026-06-09-generator-rework-hierarchical.md`. - **Статистическая модель** (распределения событий/сессия, сессий/пользователь, межсессионные паузы; нужен ли настоящий session-timeout; персистентность популяции через рестарты) **выносится в отдельную follow-up-спеку** и здесь намеренно не фиксируется.