chore(scratch): сохранены слепок трекера и handoff миграции на Gitea

- Зачем:
  - GitHub-аккаунт заблокирован 2026-07-26; трекер карты «боевой
    реализм» перенесён на git.dementev.space, контекст нужен новой
    сессии независимо от исхода апелляции.
- Что:
  - .scratch/backup/ — слепок всех 27 issues и резолюция тикета #15
    на момент блокировки;
  - .scratch/handoffs/ — handoff с состоянием карты, нюансами доступа
    к Gitea и списком хвостов (апелляция, выбор основного трекера).
- Проверка:
  - тексты читаются; ссылки на Gitea-трекер открываются
    (git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 22:03:00 +03:00
co-authored by Claude Fable 5
parent 95599ead29
commit 2e42cf63ff
3 changed files with 404 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
## Ответ
Сумма заказа живёт **на обеих сторонах**, и стороны законно расходятся:
клиентское событие несёт объявленную сумму (то, что сайт продиктовал трекеру
через dataLayer — до пересчёта на бэкенде), заказ бэкенда — итоговую правду
со статусами. Правило стенда: **поведение и атрибуцию считаем по трекеру,
деньги — по бэкенду**. Правило выучивается на конфликте: суммы у менти
не сойдутся, и он сам раскопает почему.
### Клиентская сторона (широкое событие, рамка #18)
- Таксономия торговых событий: `pageview` + `add_to_cart` + `purchase`.
Полный словарь Метрики (`detail`, `remove`, `impressions`) не берём:
механика та же, объём массивов большой, новых идей нет — остаётся теорией
в документе о реализме.
- `purchase` несёт `purchaseID/Revenue/Currency/Coupon` + массивы `product*`
состава заказа + сырую строку `ecommerce` (по образцу выгрузки Метрики,
фактура — #27).
- `add_to_cart` — массивы `product*` с одним товаром. Оживают мёртвые
счётчики `purchases`/`add_to_carts` в `v_utm_effectiveness` без правки витрины.
### Сторона бэкенда (второй источник, рамка #18)
- **Формат выгрузки: полный ежедневный слепок заказов в пределах окна
изменяемости K дней.** Запись = снапшот заказа: `order_id`, `user_id`,
времена, `status` (`created`/`paid`/`cancelled`), `updated_at`, итоговые
суммы (товары / скидка / доставка / итого) + **позиции вложенным JSON**
(`items: [{sku, qty, price}]`) — «родной» экспорт бэкенда, а не диалект
Метрики.
- Обоснование слепка (а не инкремента): очень частый боевой формат;
идемпотентный приём и самовосстановление (пропущенный день ничего не
ломает); дедуп до последней версии становится обязательным с первого дня.
Окно K делает объём защитимым (за окном заказ неизменяем — возить незачем)
и даёт границу пересчёта: выручка дня D «дышит» K дней, потом замерзает.
- Приём: дедуп через `ReplacingMergeTree`/`argMax`; разбор JSON-позиций —
**один раз** в трансформации ODS → DDS, дальше витрины работают с плоскими
массивами. Это единственный носитель навыка «вложенный JSON в ClickHouse»
в стенде.
- В `docs/generator-realism.md` — честный абзац trade-off «инкремент экономнее,
слепок надёжнее» и сноска про compacted topic как родной Kafka-паттерн
для состояния сущности.
### Сверка
- **Ключ: клиентский `purchaseID` = `order_id` бэкенда** (магазин знает номер
заказа на `/confirmation` — как `actionField.id` у Метрики).
- Конструируемые расхождения — перечислимый список из четырёх причин,
детерминированных от seed:
- **A. Отмена** — `purchase` есть, заказ дошёл до `status='cancelled'`
(бесплатно из статусов);
- **B. Потерянное событие** — заказ есть, `purchase` не доехал
(вероятность в генераторе);
- **C. Дельта суммы** — систематическая, по построению: клиент объявляет
сумму товаров до промокода и доставки, бэкенд — итог; менти может вывести
формулу связи;
- **D. Дубль события** — повторный `purchase` от обновления
`/confirmation` (вероятность).
- Пятый эффект бесплатно даёт формат доставки: **опоздание** (заказ впервые
появляется в слепке D+1/D+2) — «вчера не сходилось, сегодня сошлось».
- Не берём: сироту-фрод (дублирует B механически) и расхождение часов
клиент/сервер — это жители тумана «Грязь в данных», придут своим тикетом.
### Слои и витрины
- DDS: одна новая сущность `dds.order` (последняя версия заказа, позиции
разобраны в массивы). `purchase` отдельной сущности не получает — это
строка широкого события.
- DM, три роли: **выручка** (`v_revenue_daily`, по категориям через `dictGet`
каталога) — строится только от заказов; **сверка**
(`v_purchase_vs_orders`) — FULL OUTER JOIN по ключу с классификатором
расхождения; **атрибуция** (`v_utm_effectiveness`) — остаётся клиентской,
плюс объявленная выручка по UTM. Точный состав колонок — в спеку (#17).
### Эталонный мир
Пересборка артефакта неизбежна и уже оплачена решением #18; этот тикет
нагружает её смыслом: манифест расширяется контрольными числами заказной
стороны — заказы и выручка по дням, и ровно N потерянных / M дублей /
K отмен для самопроверки лабы сверки. Политика версионирования артефакта
здесь не решается (пункт тумана карты).
### Ограничения исполнения и страховочные срезы
Аудитория — джун после базовой программы: расхождения — перечислимый список,
не хаос. Если при сборке спеки (#17) суммарный объём испугает, резать в
порядке: (1) статусы сузить до `created`/`cancelled` — урок дедупа держится
на самом слепке; (2) расхождения вводить поэтапно — сначала A+C, потом B+D;
(3) окно K сделать константой мира, а не параметром.
### Отклонено по дороге
Событийный лог заказов (сборка автомата — дальше от типовой работы DE),
CDC-формат (имитация Debezium без Debezium), шапка+строки (воскрешает склейку
по ключам), отдельный поток возвратов, полный словарь торговых событий
Метрики, механика Sign/CollapsingMergeTree (остаётся кандидатом на потом,
её дом — клиентская сторона, поток визитов Метрики Про, не заказы).
Фактура: `docs/research/2026-07-26-yandex-clickstream-format.md` (#27),
резолюция «Модель данных» (#18).