Files
clickstream-ch-kafka-supers…/CONTEXT.md
T
ddadmin d6e7a03032 docs(specs): спека редизайна пути менти — мир из артефакта и две ветки
- Зачем:
  - находки ручного HITL 2026-07-19 требовали проектного решения: путь
    менти через backfill медленный, бедный и путаный; нужна база import
    эталонного мира и две ветки роста.
- Что:
  - спека docs/specs/2026-07-19-mentee-path-redesign.md: целевая модель
    (import + next-day + continue), эталонный 3-дневный мир в git (xz),
    переименование пульта в world_init с дефолтом import, отдельный
    world_next_day, инкрементальные счётчики manifest, один учебный
    профиль; форма работ — 4 дочерних issue.
  - CONTEXT.md: термины «мир (стенда)», «эталонный мир», «три режима
    менти».
  - .scratch/hitl-findings.md восстановлен из среза 0e312b3 как рабочий
    материал фичи (до разбора в issues).
- Проверка:
  - вычитка против hitl-findings и решений обсуждения 2026-07-19.
2026-07-19 23:18:17 +03:00

167 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CONTEXT.md — глоссарий домена
Доменный язык стенда: как мы называем сущности кликстрима и что под ними
понимаем. Один термин — одно имя. Ведётся вместе с `docs/adr/` (см.
[ADR-0001](./docs/adr/0001-spec-adr-issue-layout.md)).
## Сущности
### Пользователь (user)
`user_domain_id` (`UUID`) — постоянный идентификатор пользователя (cookie-уровень),
переживает отдельные визиты. Источник — `device_events`, привязан к `click_id`.
В витринах: `uniqExact(user_domain_id)`.
### Визит / сессия (visit / session)
`click_id` (`UUID`) — **визит**: envelope из одного и более событий с общим
контекстом device/geo. В DDL `dds.click` описана как «контекст сессии
пользователя» (см. [`sql/ddl/dds/30_dds.sql`](./sql/ddl/dds/30_dds.sql)).
Важно: в источнике **нет отдельного session-таймаута** (как Snowplow
`domain_sessionid`). Поэтому в этой модели **сессия ≡ визит ≡ `click_id`**
группа событий под одним идентификатором, а не окно активности по тайм-ауту.
Если в будущем понадобится «настоящая» сессия по 30-минутному окну неактивности —
это отдельная производная над `user_domain_id` + `event_ts`, и на сиде она дала
бы **другие** группы: в полном сиде 11 внутрисессионных пауз превышают 30 минут,
а разброс времени внутри `click_id` доходит до 49 минут (см. профиль
сид-датасета ниже; ранее здесь ошибочно значилось «разброс ≤ 1 мин» — это был
замер по малому срезу файла).
«Сессию» и «визит» используем как синонимы; в UI предпочитаем «визит», когда важно
подчеркнуть, что это не сессия-по-тайм-ауту.
### Событие (event)
`event_id` (`UUID`) — отдельное действие пользователя в рамках визита: `pageview`,
`click`, `purchase`, `add_to_cart` и т.п. (поле `event_type`). Источник —
`browser_events` + `location_events`. В витринах: `count()`.
## Иерархия
```
user_domain_id (пользователь, постоянный)
└── click_id (визит/сессия — 1..N событий с общим device/geo)
└── event_id (событие: pageview / click / purchase ...)
```
Здоровые данные дают пирамиду `users ≤ sessions ≤ events`.
## Термины потоковой генерации (steady-stream)
Относятся к синтетическому потоку из генератора (режим `steady-stream`), не к
статическому сиду. Решение и контекст — [ADR-0004](./docs/adr/0004-steady-stream-synthetic-generator.md),
форма доработки — [спека генератора](./docs/specs/2026-06-09-generator-rework-hierarchical.md).
### Популяция пользователей (user population)
Множество пользователей с **постоянными** `user_domain_id`, которые генератор
держит во времени и из которых разыгрывает активность. В отличие от сида (где
пул `user_domain_id` плоский и 1:1 с визитами), популяция — это источник
возвратов: один и тот же пользователь порождает несколько визитов.
### Возвращающийся пользователь (returning user)
Пользователь, открывающий **более одного** визита (`click_id`) во времени, с
межсессионными паузами. Именно возвраты дают расхождение `users < sessions`
то, чего нет на сиде (`users == sessions`) и что отличает поток от статики.
### Три значения слова «сид»
Слово перегружено — в разговоре про генератор это **три разные сущности**, их
нельзя путать (см. [ADR-0005](./docs/adr/0005-generator-model-clock.md)):
- **`GEN_SEED`** — зерно ГПСЧ генератора (детерминизм случайных решений). Не
данные, а число.
- **архивный статический сид** (короткое имя — «статический сид», файлы
`data/*.jsonl`) — кладовка готовых значений для генератора: браузеры,
страны, устройства, метки кампаний. По
[ADR-0006](./docs/adr/0006-generation-as-sole-analytics-source.md)
он **выведен из аналитики и стал архивным**: витрины, дашборды и курс идут через
startup-history/backfill → Kafka → STG → ODS → DDS → DM → Superset. Цель —
**совсем убрать файл**, когда генератор научится придумывать фактуру сам
(отдельная спека). Профиль ниже — теперь опорные цифры и список
известных расхождений, а не эталон для подгонки (подгонять генерацию под сид
число-в-число в ADR-0006 отклонено).
- **стартовая история стенда** (синонимы — **«стартовый сид»** и **«новый сид»**;
это не новые значения слова, а та же сущность) — сгенерированное прошлое (заливка `K → ∞` +
заморозка состояния), с которого живой стенд стартует непрерывно. По ADR-0006
она **несущая**: именно с неё свежий стенд получает историю с первой минуты.
Механизм реализован через Airflow DAG `generator_control` и служебный чистый
путь `make generated-history-analytics`; решение про часы — [ADR-0005](./docs/adr/0005-generator-model-clock.md).
### Мир (стенда)
Совокупное состояние данных стенда: события в Kafka, слои ClickHouse,
состояние генератора (модельные часы, живая популяция) и manifest с
контрольными числами. «Вырастить мир» — добавить в него модельное время
(`next-day` или живой поток), «восстановить мир» — залить его из артефакта.
### Эталонный мир (reference world)
Канонический стартовый мир менти: 3 модельных дня на профиле `daily-wave`,
собран мейнтейнером один раз, хранится сжатым артефактом в git. Все менти
импортируют один и тот же файл — числа в лабах воспроизводимы
число-в-число. Решение — [спека редизайна пути менти](./docs/specs/2026-07-19-mentee-path-redesign.md).
### Три режима менти
`import` (база: загрузить эталонный мир) и две ветки от восстановленного
состояния: `next-day` (пакетно добавить модельный день) и `continue`
(живой поток от той же границы). Это разные педагогики и разные лабы;
`backfill` — не режим менти, а инструмент мейнтейнера для сборки
эталонного мира. См. ту же спеку.
### Модельное время и масштаб (×K)
**Модельное время стенда** отвязано от настенных часов: генератор крутит
внутренние часы, а драйвер задаёт скорость — ×1 (как реальное время), ×K
(ускоренно, учебная «ручка» урока 7) или `K → ∞` (мгновенная заливка прошлого).
При ускорении «сейчас» стенда уходит вперёд настенного времени — это свойство, не
баг (ключ аналитики — `event_timestamp`). Решение и режимы —
[ADR-0005](./docs/adr/0005-generator-model-clock.md).
## Почему на статическом сиде `Unique Users == Unique Sessions`
В архивном статическом сиде (`data/*.jsonl`) каждый пользователь имеет **ровно один**
`click_id` — связь user↔визит строго 1:1 (проверено: 99 пользователей =
99 `click_id` в полном файле). Поэтому `uniqExact(user_domain_id)` и
`uniqExact(click_id)` дают одинаковое число.
Это **свойство конкретного датасета, а не закон модели и не баг расчёта.** В
реальных данных пользователь возвращается несколькими визитами, и числа
расходятся. Поэтому на дашборде мы **не** показываем две одинаковые KPI-плитки
(это читалось бы как ошибка) — вместо дубля идут производные метрики
(события на визит, доля визитов с одним событием), а различие «пользователь vs
сессия» объясняется текстом урока.
В аналитическом контуре это больше не опорный сценарий: генератор ведёт
популяцию пользователей и возвращения во времени.
## Профиль сид-датасета (измерено 2026-06-10, полные файлы)
Опорные цифры о статическом сиде (`data/*.jsonl`) — для калибровки генератора
и проверки гипотез. Измерено по **полным** файлам (1000 событий); внимание:
ранние оценки «1..7 событий на визит» делались по малому срезу и неверны.
- **Объём:** 1000 событий, 99 визитов (`click_id`), 99 пользователей (1:1).
- **Длина визита:** 1..27 событий, медиана 10, среднее 10.1.
- **Паузы между событиями визита:** медиана ~20 с, p95 ≈ 17 мин,
максимум ≈ 40 мин; пауз длиннее 30 минут — 11 штук. Разброс времени внутри
одного `click_id` — до 49 минут.
- **Воронка:** 25% визитов достигают `/confirmation`. Пути — с петлями:
возвраты на `/home`, просмотр обоих товаров; у 21 из 25 «купивших» визитов
есть события **после** `/confirmation`, у 9 — `/confirmation` встречается
дважды. То есть `/confirmation` — последний *шаг воронки*, но не обязательно
последнее *событие визита*.
- **Страницы (по событиям):** `/home` 426, `/product_a` 236, `/product_b` 157,
`/cart` 93, `/payment` 53, `/confirmation` 35. `event_type` — 100% `pageview`.
## Слои данных
`STG → ODS → DDS → DM` — см. [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md).
Сейчас витрины (`dm.*`) и дашборд Superset строятся на статическом сиде. По
[ADR-0006](./docs/adr/0006-generation-as-sole-analytics-source.md) целевой источник
аналитики — генерация (стартовая история и живой поток); перевод загрузки, витрин
и дашбордов на неё входит в текущую работу, детальный план — отдельной спекой.