- Зачем:
- клиентская сторона мира становится целой: без 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>
Зачем: код генератора компактен, но нетривиален: многое в нём — соглашения, а
не конструкции, и чтением они не выводятся. Ближайшая опасность конкретна —
следующий этап трогает и план, и день, а вставка броска в середину функции
выглядит безобидно и молча меняет весь мир.
Что: цепочка от корневого зерна до строк событий; ленивость мира — когорта по
требованию и предыстория до D0, отсюда «любой день собирается сам по себе»;
правило порядка бросков внутри подпотока с механикой и способом обнаружения
промаха; причина целочисленной случайности со ссылкой на спеку. Раздел помечен
черновым: по сути верен, но на понятность читателем со стороны не выверен —
выверка вместе с полным путеводителем, тикет #48.
Проверка: `make lint` чист, `make test` — 353 passed; правка только в README.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Зачем: план состава отдаёт дневную аудиторию, но событий у мира ещё не было.
День-функция превращает аудиторию в поток просмотров — на нём стоят лабы про
сборку визитов и про витрины, а следующий этап вешает на него торговые события.
Что:
- `day.py` — день как чистая функция зерна и номера дня: суточная волна в
местном времени посетителя, визиты по документированным правилам нарезки,
все 47 колонок выгрузки; шов для торговых событий — ряды `page` и `product`;
- `reference.py` — справочники-литералы: профили устройств, города Поволжья с
настоящими гео-id Яндекса, источники трафика, карта сайта;
- `catalog.py` и `data/catalog/products.csv` — каталог на 180 позиций, общий у
генератора и будущего словаря ClickHouse;
- `weights.py` — выбор по целым весам, один на план и на день;
- паспорт куки (устройство и город) переехал в план состава; броски приписаны
последними, поэтому измеренные числа канонического мира не сдвинулись;
- словарь: «визит» закреплён за сессией, одноимённое понятие плана стало
«днём активности»; статьи в `CONTEXT.md`;
- решения по ходу — в спеку генератора, раздел 9; наполнение
`ParsedParamsKey1` отложено тикетом #47.
Проверка: `make lint`, `make typecheck`, `make test` — 353 passed (было 297).
Счётчики плана после правки те же: приток 3827,64/день, дневная аудитория
6235–7124, 68 119 посетителей за 14 дней, 170 двухкуковых пар. День 0 —
45 810 событий за 0,6 с, снимок 14 дней — 5,9 с при пороге 30 с на день.
Две слепые линии ревью, десять находок, все закрыты и перепроверены.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Зачем:
- тикет #38: состав мира должен быть чистой функцией зерна, а счётчики
будущего манифеста — известны до генерации хоть одного события.
- Что:
- `world.py` — конфигурация мира одним модулем чистых данных: приток,
недельная волна, профиль возвратов, доли покупателей и пар, D0.
- `seeds.py` — иерархия подпотоков на `SeedSequence`: состав мира
(ось и предыстория) отдельно от дней и их компонентов.
- `plan.py` — ленивый план состава: когорта дня, аудитория дня из
окна возвратов, гарантированные заказы пар, счётчики горизонта.
Случайность — только целыми числами.
- спека, раздел 1: вторая кука пары рождается по затухающему профилю
возвратов; раздел 9: измеренные числа канонического мира, оценка
накопленной аудитории поправлена с ≈60 до 68 тыс.
- CONTEXT.md: термин «когорта дня»; README генератора — новые модули.
- Проверка:
- make test (295 тестов), make lint, make typecheck;
- тесты проверены мутациями: 12 подмен в плане и конфигурации,
каждая роняет ровно свой тест.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Зачем:
- код генератора будет расти в #38/#39 — проверку типов лучше
завести до реализации, чем типизировать задним числом.
- Что:
- ty добавлен dev-зависимостью генератора (закреплён в uv.lock).
- в Makefile добавлена цель typecheck, README обеих сторон обновлены.
- Проверка:
- make typecheck && make lint && make test — всё зелено, 248 тестов.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Зачем:
- этап 2 начинается с формы: контракт схемы — источник истины и для
генерации событий, и для DDL хранилища, а имена пакета и модулей
задают границы всем следующим тикетам этапа.
- Что:
- заведён uv-проект generator/ (pyproject.toml и uv.lock в git; numpy,
pytest и ruff), пакет clickstream_generator.
- schema.py — контракт: чистые данные о 47 колонках выгрузки (имя
Метрики, тип ClickHouse, тип numpy, имя для DDS, группа); порядок
несёт сам кортеж COLUMNS, отдельного поля с номером нет намеренно.
- schema_doc.py собирает из контракта описание выгрузки
docs/formats/clickstream-event.md — по нему пишется сторона
хранилища; документ руками не правится.
- тесты: инварианты контракта (состав, уникальность, заполненность,
согласие типов и порядок групп) и свежесть описания выгрузки.
- цели make lint, make test и make docs; README, AGENTS.md и
CONTEXT.md дополнены генератором, форматами и словарной статьёй.
- Проверка:
- make test (248 тестов), make lint, make config-test;
- make docs, затем git diff --exit-code docs/ — пусто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>