- Зачем: - тикет #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>
69 lines
5.0 KiB
Python
69 lines
5.0 KiB
Python
"""Конфигурация мира: все числа, которыми задан модельный магазин.
|
|
|
|
Модуль — приглашение крутить: поменяйте число, пересоберите снимок и
|
|
посмотрите, что стало с данными. Правка любой константы здесь — смена мира,
|
|
поэтому чек манифеста честно покраснеет: манифест сторожит только канонический
|
|
мир, свои миры менти собирает без его гарантий (спека генератора, раздел 9).
|
|
|
|
Числа решены спекой и связаны между собой; связки сторожат тесты
|
|
`test_world.py`, чтобы правка одного числа не рассыпала вывод соседнего.
|
|
Здесь только чистые данные — как в контракте схемы, никакой логики.
|
|
"""
|
|
|
|
from datetime import date
|
|
|
|
# D0 — первый день оси модельного времени, понедельник. Реальный календарь в
|
|
# модели не участвует: дата нужна лишь затем, чтобы дни оси легли в
|
|
# `EventDate`/`UTCEventTime` конкретными числами. От даты запуска мир не
|
|
# зависит — иначе манифест перестал бы быть воспроизводимым.
|
|
ORIGIN = date(2026, 6, 1)
|
|
|
|
# Приток: сколько новых людей приходит в мир в средний день. Каждый приводит
|
|
# свою куку, поэтому число это же — приток кук; вторые куки двухкуковых пар
|
|
# добавляют к нему меньше процента.
|
|
DAILY_INFLUX = 3_800
|
|
|
|
# Недельная волна притока, проценты от среднего: понедельник … воскресенье.
|
|
# Модулируется тем же профилем, что трафик, — иначе доля новичков скакала бы
|
|
# по дням недели. В сумме ровно 700: за неделю средний день остаётся средним.
|
|
WEEKLY_INFLUX_PERCENT = (105, 108, 107, 105, 95, 88, 92)
|
|
|
|
# Разброс притока изо дня в день, проценты: ровный приток выдал бы себя в
|
|
# первом же графике по дням.
|
|
INFLUX_JITTER_PERCENT = 5
|
|
|
|
# Доля одноразовых кук: пришли раз и не вернулись — как в живом трафике.
|
|
ONE_SHOT_PERCENT = 75
|
|
|
|
# Сколько раз возвращается кука, которая вернулась хоть раз: веса для 1, 2,
|
|
# 3 … возвратов. В среднем выходит 3–4 возврата; вместе с одноразовыми это
|
|
# ≈1,9 активного дня на куку — отсюда дневная аудитория 6–8 тыс. при
|
|
# притоке 3 800 (спека, разделы 5 и 9).
|
|
RETURN_COUNT_WEIGHTS = (25, 20, 15, 12, 9, 7, 5, 4, 2, 1)
|
|
|
|
# Хвост возвратов: окно активности куки от её первого визита. За краем окна
|
|
# кука не возвращается. Оно же — глубина предыстории: столько когорт живёт
|
|
# до D0, чтобы дневная аудитория была на полке с самого первого дня.
|
|
RETURN_TAIL_DAYS = 90
|
|
|
|
# Профиль возвратов по дням от первого визита: почти всё в первую неделю,
|
|
# дальше тонкий хвост до края окна — повторные покупки в магазине случаются
|
|
# и через месяцы. Профиль затухает к краю, поэтому обрыв на нём в данных не
|
|
# виден. Читается по парам «сколько дней — с каким весом»; дней в сумме
|
|
# ровно `RETURN_TAIL_DAYS`.
|
|
RETURN_DELAY_WEIGHTS = tuple(
|
|
weight
|
|
for days, weight in ((3, 100), (7, 40), (20, 8), (60, 1))
|
|
for _ in range(days)
|
|
)
|
|
|
|
# Доля покупателей среди людей когорты. Считается людьми, не куками: человек
|
|
# с двумя куками — один покупатель. Число плана, а не торгового поведения:
|
|
# без него не отобрать двухкуковые пары.
|
|
BUYER_PERCENT = 5
|
|
|
|
# Доля покупателей, у которых заведётся вторая кука (мастер-спека, раздел 5).
|
|
# Такой паре план назначает по гарантированному заказу с каждой куки — на
|
|
# этом стоит лаба про склейку личности.
|
|
PAIRED_BUYER_PERCENT = 15
|