Торговые события #40

Closed
opened 2026-08-01 21:19:40 +03:00 by ddmitry · 3 comments
Owner

Part of #4.

Цель

День-функция, часть вторая: таксономия торговых событий. add_to_cart и
purchase вплетаются в сессии, появляется каталог товаров — клиентская
сторона становится целой: широкое событие, таксономия, анонимность, N:1.

Что войдёт

  • add_to_cart: массивы product* с одним товаром; GoalsReached
    (две цели: корзина и покупка — цели дублируют события, это нормально).
  • purchase: состав заказа, блок purchase*, ровно один элемент в
    purchaseID; сырой JSON ecommerce, как отдаёт Метрика.
  • Деньги: внутри — целые копейки; Float64 — только представление в
    клиентском purchase (урок о расхождениях).
  • Двухкуковые покупатели: по плану состава — минимум по одной покупке
    с каждой куки.
  • Анонимность: у события только ClientID, никаких email и user_id.

Критерии приёмки

  • У purchase массив purchaseID несёт ровно один элемент (тест).
  • Двухкуковые пары сходятся с планом состава; покупки — не с планом, а с измеренным днём и вилкой конверсии (решение при исполнении, спека §9) (тест).
  • Деньги в генераторе целые; Float64 появляется только на
    представлении (тест).
  • Массивы product* одной длины; ecommerce — валидный JSON,
    согласованный с массивами (тест).
  • Детерминизм: два прогона — идентичные события; правка торгового
    подпотока не сдвигает трафиковый (иерархия подпотоков, тест).
  • Заказ уже корзины: часть положенных позиций не куплена (тест).
  • Помеченные планом покупатели покупают повторно заметно чаще прочих (тест).
  • Дневная аудитория в вилке 6–8 тыс., средний день около 50 тыс. событий, конверсия визита около 2% (тест).

Границы

  • Заказы бэкенда (слепки, топик orders) — этап 3.
  • Расхождения A–D и опоздания — этапы 4 и 6: здесь клиентская сторона
    честная, без потерь и дублей; структура подпотоков лишь не должна им
    мешать.

Сначала прочитать

  • docs/specs/2026-08-01-generator.md — разделы 1, 2, 6.
  • docs/specs/2026-07-30-stand-v2-realism.md — разделы 1.2 (ecommerce),
    3, 4, 5.
  • data/catalog/products.csv приходит готовым из #39, здесь он только
    читается.

Проверка

  • make test
Part of #4. ## Цель День-функция, часть вторая: таксономия торговых событий. `add_to_cart` и `purchase` вплетаются в сессии, появляется каталог товаров — клиентская сторона становится целой: широкое событие, таксономия, анонимность, N:1. ## Что войдёт - `add_to_cart`: массивы `product*` с одним товаром; `GoalsReached` (две цели: корзина и покупка — цели дублируют события, это нормально). - `purchase`: состав заказа, блок `purchase*`, ровно один элемент в `purchaseID`; сырой JSON `ecommerce`, как отдаёт Метрика. - Деньги: внутри — целые копейки; Float64 — только представление в клиентском `purchase` (урок о расхождениях). - Двухкуковые покупатели: по плану состава — минимум по одной покупке с каждой куки. - Анонимность: у события только `ClientID`, никаких email и user_id. ## Критерии приёмки - [x] У `purchase` массив `purchaseID` несёт ровно один элемент (тест). - [x] Двухкуковые пары сходятся с планом состава; покупки — не с планом, а с измеренным днём и вилкой конверсии (решение при исполнении, спека §9) (тест). - [x] Деньги в генераторе целые; Float64 появляется только на представлении (тест). - [x] Массивы `product*` одной длины; `ecommerce` — валидный JSON, согласованный с массивами (тест). - [x] Детерминизм: два прогона — идентичные события; правка торгового подпотока не сдвигает трафиковый (иерархия подпотоков, тест). - [x] Заказ уже корзины: часть положенных позиций не куплена (тест). - [x] Помеченные планом покупатели покупают повторно заметно чаще прочих (тест). - [x] Дневная аудитория в вилке 6–8 тыс., средний день около 50 тыс. событий, конверсия визита около 2% (тест). ## Границы - Заказы бэкенда (слепки, топик `orders`) — этап 3. - Расхождения A–D и опоздания — этапы 4 и 6: здесь клиентская сторона честная, без потерь и дублей; структура подпотоков лишь не должна им мешать. ## Сначала прочитать - docs/specs/2026-08-01-generator.md — разделы 1, 2, 6. - docs/specs/2026-07-30-stand-v2-realism.md — разделы 1.2 (ecommerce), 3, 4, 5. - `data/catalog/products.csv` приходит готовым из #39, здесь он только читается. ## Проверка - `make test`
ddmitry added the ready-for-agent label 2026-08-01 21:20:02 +03:00
ddmitry added a new dependency 2026-08-01 21:20:06 +03:00
ddmitry changed title from Торговые события и каталог товаров to Торговые события 2026-08-02 16:35:12 +03:00
Author
Owner

Решения перед реализацией приняты в грилинге 2026-08-02 и записаны в спеку генератора, раздел 9 (блок «Решено при исполнении #40»). Здесь — только то, что меняет постановку тикета.

Выход за исходные границы. Метка «покупатель» из плана состава до сих пор ни на что не влияла: кто купит, решал одинаковый для всех бросок в дне. Решено сделать её значимой — помеченный доходит до заказа заметно чаще прочих. Воронка живёт в коде #39, поэтому #40 её правит и сдвигает трафиковый поток. Сейчас это ничего не стоит: манифест ещё не собран (#42).

Что добавилось к составу работы:

  • корзина шире заказа: посетитель бросает около 15% положенных позиций;
  • событие корзины садится на карточку товара, а не на страницу корзины;
  • номер заказа читаемый — день и порядковый номер покупки в дне; он же order_id бэкенда;
  • промокод в части заказов есть, но в клиентскую сумму не входит;
  • цена товара в событии — целые рубли (внутри генератора копейки), дробное число одно: purchaseRevenue;
  • зависимость orjson появляется здесь, а не в #41: сырой ecommerce собирает канонический сериализатор.

Критерии приёмки прирастают тремя:

  • Заказ уже корзины: часть положенных позиций не куплена (тест).
  • Помеченные планом покупатели покупают заметно чаще прочих (тест).
  • Общее число покупок в дне и доля визитов с корзиной не сдвинулись (тест).

Хвосты соседним этапам (записаны в раздел 8 спеки): заказу с промокодом бэкенд обязан дать скидку (этап 3); расхождение сумм само не появится — цены каталога кратны рублю, Float64 не плывёт, класс amount_delta придётся создавать намеренно (этап 4).

Ветка задачи: feat/40-trade-events.

Решения перед реализацией приняты в грилинге 2026-08-02 и записаны в спеку генератора, раздел 9 (блок «Решено при исполнении #40»). Здесь — только то, что меняет постановку тикета. **Выход за исходные границы.** Метка «покупатель» из плана состава до сих пор ни на что не влияла: кто купит, решал одинаковый для всех бросок в дне. Решено сделать её значимой — помеченный доходит до заказа заметно чаще прочих. Воронка живёт в коде #39, поэтому #40 её правит и сдвигает трафиковый поток. Сейчас это ничего не стоит: манифест ещё не собран (#42). **Что добавилось к составу работы:** - корзина шире заказа: посетитель бросает около 15% положенных позиций; - событие корзины садится на карточку товара, а не на страницу корзины; - номер заказа читаемый — день и порядковый номер покупки в дне; он же `order_id` бэкенда; - промокод в части заказов есть, но в клиентскую сумму не входит; - цена товара в событии — целые рубли (внутри генератора копейки), дробное число одно: `purchaseRevenue`; - зависимость `orjson` появляется здесь, а не в #41: сырой `ecommerce` собирает канонический сериализатор. **Критерии приёмки прирастают тремя:** - [ ] Заказ уже корзины: часть положенных позиций не куплена (тест). - [ ] Помеченные планом покупатели покупают заметно чаще прочих (тест). - [ ] Общее число покупок в дне и доля визитов с корзиной не сдвинулись (тест). **Хвосты соседним этапам** (записаны в раздел 8 спеки): заказу с промокодом бэкенд обязан дать скидку (этап 3); расхождение сумм само не появится — цены каталога кратны рублю, `Float64` не плывёт, класс `amount_delta` придётся создавать намеренно (этап 4). Ветка задачи: `feat/40-trade-events`.
Author
Owner

Холодное ревью первой редакции решений нашло две дыры уровня решений. Обе закрыты, спека переписана — читать раздел 9, блок «Решено при исполнении #40». Здесь только то, что меняет постановку против предыдущего комментария.

1. Метка покупателя получает второй рычаг. Одного лифта конверсии мало: метки в событии нет (кликстрим анонимен), а кука живёт меньше двух визитов за снимок — второй покупке негде случиться. Лифт поднял бы долю кук с двумя покупками с 2% до 6%, и «постоянный покупатель» так и не появился бы. Поэтому помеченные ещё и дольше живут и чаще возвращаются — это правка плана состава (#38), а не только воронки (#39).

2. Копейки в части цен каталога. Сейчас все 180 цен кратны рублю, поэтому копеечная дисциплина существует лишь на словах, а урок про Float64 беспредметен. Часть цен получает копейки: productPrice округляется самим форматом, purchaseRevenue несёт точную сумму. Правка data/catalog/products.csv — файл заведён в #39.

Границы работы, итог. Тикет правит три чужих участка: план состава (#38), воронку (#39) и каталог (#39). Манифест ещё не собран (#42), поэтому сдвиг потока сейчас ничего не стоит.

Критерии приёмки — замена. Предыдущий комментарий предлагал «общее число покупок и доля визитов с корзиной не сдвинулись». Он больше не годится: аудитория намеренно подрастёт. Вместо него:

  • Дневная аудитория остаётся в вилке 6–8 тыс., средний день — около 50 тыс. событий, конверсия визита — около 2% (тест).
  • Помеченные планом покупатели покупают повторно заметно чаще прочих (тест).
  • Заказ уже корзины: часть положенных позиций не куплена (тест).

База до правок, средний день канонического мира: аудитория 6 710, визитов 9 249, событий 44 436, визитов с корзиной 780 (8,4%), с покупкой 221 (2,4%). После правок числа состава мира в спеке перемеряются и переписываются.

Оговорка к исходному критерию. «Счётчики покупок и двухкуковых пар сходятся с планом состава» — счётчика покупок в плане нет (PlanCounters), и раздел 1 спеки прямо говорит, что торговые счётчики появятся только здесь. Сходятся пары; счётчик покупок либо заводится в плане этим тикетом, либо критерий читается про пары.

Холодное ревью первой редакции решений нашло две дыры уровня решений. Обе закрыты, спека переписана — читать раздел 9, блок «Решено при исполнении #40». Здесь только то, что меняет постановку против предыдущего комментария. **1. Метка покупателя получает второй рычаг.** Одного лифта конверсии мало: метки в событии нет (кликстрим анонимен), а кука живёт меньше двух визитов за снимок — второй покупке негде случиться. Лифт поднял бы долю кук с двумя покупками с 2% до 6%, и «постоянный покупатель» так и не появился бы. Поэтому помеченные ещё и дольше живут и чаще возвращаются — это правка плана состава (#38), а не только воронки (#39). **2. Копейки в части цен каталога.** Сейчас все 180 цен кратны рублю, поэтому копеечная дисциплина существует лишь на словах, а урок про Float64 беспредметен. Часть цен получает копейки: `productPrice` округляется самим форматом, `purchaseRevenue` несёт точную сумму. Правка `data/catalog/products.csv` — файл заведён в #39. **Границы работы, итог.** Тикет правит три чужих участка: план состава (#38), воронку (#39) и каталог (#39). Манифест ещё не собран (#42), поэтому сдвиг потока сейчас ничего не стоит. **Критерии приёмки — замена.** Предыдущий комментарий предлагал «общее число покупок и доля визитов с корзиной не сдвинулись». Он больше не годится: аудитория намеренно подрастёт. Вместо него: - [ ] Дневная аудитория остаётся в вилке 6–8 тыс., средний день — около 50 тыс. событий, конверсия визита — около 2% (тест). - [ ] Помеченные планом покупатели покупают повторно заметно чаще прочих (тест). - [ ] Заказ уже корзины: часть положенных позиций не куплена (тест). База до правок, средний день канонического мира: аудитория 6 710, визитов 9 249, событий 44 436, визитов с корзиной 780 (8,4%), с покупкой 221 (2,4%). После правок числа состава мира в спеке перемеряются и переписываются. **Оговорка к исходному критерию.** «Счётчики покупок и двухкуковых пар сходятся с планом состава» — счётчика покупок в плане нет (`PlanCounters`), и раздел 1 спеки прямо говорит, что торговые счётчики появятся только здесь. Сходятся пары; счётчик покупок либо заводится в плане этим тикетом, либо критерий читается про пары.
Author
Owner

Работа выполнена в ветке feat/40-trade-events, тесты 353 → 383, make lint
и make typecheck зелёные.

Перемеренные числа канонического мира: аудитория 6 637–7 595 (в среднем
7 166), визитов 9 879, событий 49 509, визитов с корзиной 765 (7,7%), с
покупкой 240 (2,4%), пар в горизонте 192 — все три вилки раздела 5
выдержаны.

Два слепых ревью дали 9 находок (2 MAJOR, 5 MINOR, 2 NIT), все закрыты,
перепроверки обеих линий — ALL_CLOSED.

Из находок стоит назвать две: полночь могла срезать покупку, оставив
страницу подтверждения, — теперь граница суток забирает подтверждение
вместе с его покупкой; и четыре сторожа, проходивших при подмене
проверяемого, — усилены мутантами.

Решения, записанные в спеку §9 этим тикетом: счётчик покупок в план не
заводится (сверяются пары), CART_PERCENT опущен с 8 до 6 как
единственное число мира вне двух рычагов, резерв суток под торговый
хвост.

Работа выполнена в ветке `feat/40-trade-events`, тесты 353 → 383, `make lint` и `make typecheck` зелёные. Перемеренные числа канонического мира: аудитория 6 637–7 595 (в среднем 7 166), визитов 9 879, событий 49 509, визитов с корзиной 765 (7,7%), с покупкой 240 (2,4%), пар в горизонте 192 — все три вилки раздела 5 выдержаны. Два слепых ревью дали 9 находок (2 MAJOR, 5 MINOR, 2 NIT), все закрыты, перепроверки обеих линий — ALL_CLOSED. Из находок стоит назвать две: полночь могла срезать покупку, оставив страницу подтверждения, — теперь граница суток забирает подтверждение вместе с его покупкой; и четыре сторожа, проходивших при подмене проверяемого, — усилены мутантами. Решения, записанные в спеку §9 этим тикетом: счётчик покупок в план не заводится (сверяются пары), `CART_PERCENT` опущен с 8 до 6 как единственное число мира вне двух рычагов, резерв суток под торговый хвост.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#40