Торговые события генератора: корзина, покупка и уровень спроса товара #51

Merged
ddmitry merged 8 commits from feat/40-trade-events into main 2026-08-02 22:59:28 +03:00
Owner

Торговая половина генератора: события корзины и покупки, а следом — правка
двух точных равенств, которые нашла приёмка.

Closes #40

Тикет #50 закрыт руками, его работа входит в эту же ветку.

Что вошло

Торговые события (#40). Событие корзины садится на карточку товара,
покупка — на страницу подтверждения. Деньги считаются целыми копейками, в
productPrice ложатся целые рубли, а точная сумма живёт в
purchaseRevenue — из-за этого сумма по разобранным массивам честно
расходится с выручкой, и урок про Float64 перестаёт быть выдуманным. Номер
заказа ГГГГММДД-NNNN присваивается всякой покупке, дошедшей до потока дня.
Сырой ecommerce несёт больше плоских колонок: бренд, вариант,
actionField.

Корзина и уровень спроса (#50). Приёмка #40 показала два равенства,
каких в живом магазине не бывает: событие корзины случалось ровно у визитов
со страницей /cart, а внутри такого визита в корзину уходили все открытые
карточки. Теперь класть может любой визит, открывший карточку — 41,4% таких
визитов корзину не открывают вовсе; у визита, дошедшего до /cart, корзина
по-прежнему непуста. У товара появился уровень спроса: колонка каталога с
тремя значениями и два ряда вероятностей в числах мира. Конверсия
«просмотр карточки → корзина» стала различима — 13,1% / 9,2% / 5,8% по
уровням, по отдельным товарам от 15,4% до 5,1%.

Числа мира после правки (14 дней канонического зерна)

Дневная аудитория 6637–7595, визитов 9879, средний день 49 834 события,
конверсия визита в покупку 2,43%, конверсия карточки в корзину 8,62%,
позиций на заказ 1,74, однопозиционных заказов 56,4%, брошено 7,25% позиций
в покупающих корзинах.

Как проверялось

make lint && make typecheck && make test из корня репозитория — 394
passed
(было 297 до ветки).

Трафиковая половина побайтово не сдвинулась: sha256 по (URL, времени,
WatchID) всех просмотров за 14 дней совпадает со снимком до правки.

Обе задачи прошли конвейер целиком: ревью постановки, реализация, саморевью,
два слепых ревью разными родословными, триаж у исполнителя, узкие
перепроверки теми же ревьюерами и приёмка своими глазами. По #40 — 9 находок,
9 закрыто; по #50 — 9 находок, 9 закрыто, ноль отклонённых.

Что решено по ходу и записано в спеку

Решения обеих задач лежат в docs/specs/2026-08-01-generator.md, раздел 9 —
блоки «Решено при исполнении #40», «#40 после приёмки» и «#50». Там же
записаны отклонённые варианты и решение владельца о том, что вилка конверсии
в корзину — ориентир, а не граница: настоящий предел здесь дневной бюджет
событий, на котором стоят манифест (#42) и порог скорости дня.

Хвост соседям: словарь ClickHouse над products.csv (#37/#43) должен взять
новую колонку сразу, а манифесту (#42) нужен хеш каталога — в файле теперь
живёт не только форма данных, но и поведение.

Торговая половина генератора: события корзины и покупки, а следом — правка двух точных равенств, которые нашла приёмка. Closes #40 Тикет #50 закрыт руками, его работа входит в эту же ветку. ## Что вошло **Торговые события (#40).** Событие корзины садится на карточку товара, покупка — на страницу подтверждения. Деньги считаются целыми копейками, в `productPrice` ложатся целые рубли, а точная сумма живёт в `purchaseRevenue` — из-за этого сумма по разобранным массивам честно расходится с выручкой, и урок про Float64 перестаёт быть выдуманным. Номер заказа `ГГГГММДД-NNNN` присваивается всякой покупке, дошедшей до потока дня. Сырой `ecommerce` несёт больше плоских колонок: бренд, вариант, `actionField`. **Корзина и уровень спроса (#50).** Приёмка #40 показала два равенства, каких в живом магазине не бывает: событие корзины случалось ровно у визитов со страницей `/cart`, а внутри такого визита в корзину уходили все открытые карточки. Теперь класть может любой визит, открывший карточку — 41,4% таких визитов корзину не открывают вовсе; у визита, дошедшего до `/cart`, корзина по-прежнему непуста. У товара появился уровень спроса: колонка каталога с тремя значениями и два ряда вероятностей в числах мира. Конверсия «просмотр карточки → корзина» стала различима — 13,1% / 9,2% / 5,8% по уровням, по отдельным товарам от 15,4% до 5,1%. ## Числа мира после правки (14 дней канонического зерна) Дневная аудитория 6637–7595, визитов 9879, средний день 49 834 события, конверсия визита в покупку 2,43%, конверсия карточки в корзину 8,62%, позиций на заказ 1,74, однопозиционных заказов 56,4%, брошено 7,25% позиций в покупающих корзинах. ## Как проверялось `make lint && make typecheck && make test` из корня репозитория — **394 passed** (было 297 до ветки). Трафиковая половина побайтово не сдвинулась: sha256 по (URL, времени, `WatchID`) всех просмотров за 14 дней совпадает со снимком до правки. Обе задачи прошли конвейер целиком: ревью постановки, реализация, саморевью, два слепых ревью разными родословными, триаж у исполнителя, узкие перепроверки теми же ревьюерами и приёмка своими глазами. По #40 — 9 находок, 9 закрыто; по #50 — 9 находок, 9 закрыто, ноль отклонённых. ## Что решено по ходу и записано в спеку Решения обеих задач лежат в `docs/specs/2026-08-01-generator.md`, раздел 9 — блоки «Решено при исполнении #40», «#40 после приёмки» и «#50». Там же записаны отклонённые варианты и решение владельца о том, что вилка конверсии в корзину — ориентир, а не граница: настоящий предел здесь дневной бюджет событий, на котором стоят манифест (#42) и порог скорости дня. Хвост соседям: словарь ClickHouse над `products.csv` (#37/#43) должен взять новую колонку сразу, а манифесту (#42) нужен хеш каталога — в файле теперь живёт не только форма данных, но и поведение.
ddmitry added 8 commits 2026-08-02 22:57:26 +03:00
- Зачем:
  - перед реализацией торговых событий грилинг закрыл шесть развилок; без
    записи решения и отклонённые варианты потерялись бы, а часть из них
    выходит за границы тикета и меняет постановку.
- Что:
  - в раздел 9 спеки генератора добавлен блок «Решено при исполнении #40»:
    корзина шире заказа, метка покупателя становится значимой, место
    торговых событий, номер заказа, промокод, единицы денег, разная длина
    массивов purchase* и product*, orjson, хвост покупки у границы суток.
  - в раздел 8 добавлены хвосты этапам 3 и 4: скидка по промокоду и
    намеренное расхождение сумм.
  - в словарь добавлены «покупатель» и «торговое событие».
- Проверка:
  - чтением: docs/specs/2026-08-01-generator.md, разделы 8 и 9; CONTEXT.md.
- Зачем:
  - холодное ревью нашло две дыры уровня решений: лифт конверсии не делает
    покупателя видимым в данных, а цены каталога кратны рублю — от этого
    урок про Float64 остаётся без материала.
- Что:
  - метка покупателя получает второй рычаг: помеченные дольше живут и чаще
    возвращаются; границы правки заданы вилками раздела 5, база до правок
    записана числами.
  - часть цен каталога получит копейки; хвост этапу 4 переписан честнее.
  - дописаны нерешённые места: количество штук и повторная карточка,
    множество нумерации заказов, таблица «код — скидка», состав сырого
    ecommerce, резерв времени вместо зажима к полуночи.
  - два расхождения внесены в мастер-спеку и записаны в раздел 7: длина
    массивов purchase* и product*, механика класса amount_delta.
- Проверка:
  - чтением: docs/specs/2026-08-01-generator.md, разделы 7-9;
    docs/specs/2026-07-30-stand-v2-realism.md, разделы 1, 4, 7.
- Зачем:
  - холодное ревью отметило, что это решение, а не калибровка: от выбора
    шага зависит, как «покупатель» читается в данных, и оставлять выбор
    реализации нельзя.
- Что:
  - в блоке решений #40 записано, что помеченный отличается на обоих шагах
    воронки — и до корзины доходит чаще, и бросает её реже; числа под
    каждый шаг остаются калибровкой.
- Проверка:
  - чтением: docs/specs/2026-08-01-generator.md, раздел 9, блок #40.
- Зачем:
  - клиентская сторона мира становится целой: без add_to_cart и purchase
    в данных нет ни таксономии событий, ни вложенного JSON, ни денег,
    а метка «покупатель» из плана состава ни на что не влияла (#40).
- Что:
  - добавлен модуль commerce: корзина шире заказа, номер заказа вида
    ГГГГММДД-NNNN, промокод без скидки в сумме, сырой ecommerce через orjson;
  - метка покупателя получила два рычага — долгую жизнь куки в плане и
    свою воронку в дне; CART_PERCENT опущен с 8 до 6, чтобы конверсия
    мира осталась около 2%;
  - часть цен каталога получила копейки: productPrice округляется форматом,
    purchaseRevenue несёт точную сумму — расхождение живёт внутри события;
  - граница суток забирает страницу подтверждения вместе с её покупкой:
    потерь на клиентской стороне этот этап не заводит;
  - решения и перемеренные числа мира записаны в спеку генератора, §9.
- Проверка:
  - make lint && make typecheck && make test — 383 passed (было 353).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
  - приёмка #40 нашла в торговых данных два точных равенства, каких в живом
    магазине не бывает: событие корзины возникало ровно у визитов со
    страницей /cart, а внутри такого визита в корзину ложились все открытые
    карточки.
- Что:
  - в спеку генератора, раздел 9, добавлен блок «Решено при исполнении #40
    после приёмки»: корзина отвязана от страницы корзины, уровень спроса
    становится свойством товара, границы правки — вилки раздела 5.
  - записаны отклонённые варианты: свой процент у каждого sku и вывод
    склонности из цены.
  - в CONTEXT.md заведён термин «уровень спроса».
- Проверка:
  - git show --stat; текст решений читается без обращения к переписке.
- Зачем:
  - блок «Решено при исполнении #40 после приёмки» цитировал замер на зерне 0,
    а канонический мир, на котором стоят все тесты, — CANONICAL_SEED
    (20260601); числа относились к другому миру.
- Что:
  - замер перемерен на каноническом зерне за 14 дней: тождество «событие
    корзины ↔ страница /cart» держится каждый день, на дне 2 это 828 и 828.
  - уточнены средний день (49 509 событий), конверсия визита (2,43%), состав
    заказа (1,87 позиции) и доля брошенных позиций (число мира 15%, замер по
    покупающим корзинам 12%).
- Проверка:
  - скрипт замера на 14 днях CANONICAL_SEED; git show --stat.
- Зачем:
  - приёмка #40 нашла в торговых данных два точных равенства, каких в живом
    магазине не бывает: событие корзины случалось ровно у визитов со
    страницей /cart, а внутри такого визита в корзину уходили все открытые
    карточки. Привлекательность товара было нечем измерить, а аналитик читал
    бы эти равенства как склейку в разметке.
- Что:
  - класть в корзину может любой визит, открывший карточку; страница /cart
    осталась шагом воронки, а у визита, дошедшего до неё, корзина непуста.
  - у товара появился уровень спроса — колонка каталога с тремя значениями
    и два ряда вероятностей в числах мира: намерение визита берётся из шага
    воронки, а не из метки покупателя, которая уже действует через неё.
  - уровни рассыпаны по каталогу одной колодой: одинакового расклада по
    категориям нет, связи с ценой нет, и то и другое сторожится тестами.
  - числа мира перемерены: конверсия карточки в корзину 8,62% (по уровням
    13,1 / 9,2 / 5,8), кладут без открытия корзины 41,4% таких визитов,
    средний день 49 834 события, конверсия визита в покупку 2,43%.
  - спека генератора и мастер-спека приведены к новой форме каталога.
- Проверка:
  - make lint && make typecheck && make test — 394 passed (было 383).
  - трафиковая половина побайтово та же: sha256 по (URL, времени, WatchID)
    всех просмотров за 14 канонических дней совпадает со снимком до правки.
- Зачем:
  - после #50 доля просмотров, доходящих до корзины, подошла к краю вилки
    5–9%, и следующий исполнитель стал бы калибровать поведение под это
    число. Владелец решил: пока доля правдоподобна, она ничего не сторожит —
    у живых магазинов она гуляет широко.
- Что:
  - в блок «Решено при исполнении #50» добавлено решение владельца: вилка —
    ориентир; настоящий предел здесь дневной бюджет событий, на котором
    стоят манифест (#42) и порог скорости дня.
- Проверка:
  - git show --stat; следующий исполнитель читает решение, не переспрашивая.
ddmitry merged commit a18e6b7a80 into main 2026-08-02 22:59:28 +03:00
ddmitry deleted branch feat/40-trade-events 2026-08-02 22:59:29 +03:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ddmitry/clickstream-data-platform#51