- Зачем: - генератор даёт здоровую пирамиду, и стенду нужен единый источник аналитики вместо вырожденного статического сида. - Что: - добавлен ADR-0006: генерация — единственный источник аналитики, статический сид становится архивным (кладовка значений до синтеза фактуры). - добавлена спека модельного времени: точка отсчёта, заливка прошлого, стартовая история, сохранение состояния, воспроизводимость и проверка в два шага. - в ADR-0004 и ADR-0005 добавлены указатели вперёд на ADR-0006 и спеку. - Проверка: - чтение документов; перекрёстные ссылки между ADR-0004/0005/0006 и спекой согласованы. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.9 KiB
ADR-0005: Модельные часы генератора, отвязанные от настенного времени
Принято: 2026-06-11
Статус: accepted
Связано: ADR-0004 (расширяет —
steady-stream как синтетический генератор), CONTEXT.md
(термины времени и «сида»), мат-спека
docs/specs/2026-06-10-generator-math-model.md
(разделы «Персистентность» и «Воспроизводимость» написаны от настенных часов —
будут реконсилированы отдельным заходом), задача
06-state-v2-and-restart
(state v2 и правило рестарта построены на настенных часах).
Обновление (2026-06-14): направление «стартовая история стенда» запущено в ADR-0006; реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки выполнена в спеке модельного времени.
Решение
Модельное время генератора расцеплено от настенных часов (now()).
Генератор потребляет метку такта извне; «драйвер часов» задаёт отображение
модельного времени на реальное. Шов под это уже существует в коде (такт
принимает метку времени параметром).
Три режима — это драйверы поверх одного шва, а не отдельные архитектуры:
- живой ×1 — модельное время идёт со скоростью настенных часов (текущее поведение);
- живой ускоренный ×K — модельное время идёт в
Kраз быстрее реального (K— конфигурация, дефолт ×1); - заливка ×∞ — модельное время гонится без сна от стартовой точки до
целевой; частный случай
K → ∞.
Живой стенд масштабируется ×K, дефолт ×1. Ускорение — учебная «ручка» урока 7: разогнать стенд и вживую увидеть медленные явления (возвраты, межсессионные паузы, сдвиг воронки после правки марковской таблицы переходов).
Метки событий пишутся по модельному времени. При ускорении/заливке «сейчас»
стенда расходится с настенными часами — для аналитического стенда (ключ —
event_timestamp, внеочередная вставка в ClickHouse нормальна) это безвредно;
фиксируем как явное свойство, чтобы «время стенда ≠ твои настенные часы» не
читалось как баг.
Имена параметров, формат конфигурации и алгоритм драйвера — за исполнителем; здесь не фиксируются.
Контекст
Текущая модель неявно держит инвариант «время генератора ≡ реальное время ×1»:
главный цикл спит до настенного такта, метки событий = now() + смещения, а
паузы внутри визита «проживаются» в реальном времени. Для учебного плана это
неудобно по двум причинам:
- Медленные явления не успеть показать. Возвраты (~2 ч межсессионной паузы), накопление воронки, удержание разворачиваются часами и днями — в рамках урока на ×1 их не увидеть.
- Историческую глубину живой генератор не создаёт вовсе. За реальную минуту рождается ~λ событий реального времени; глубину сейчас даёт только статический сид.
Дополнительно: воспроизводимость на ×1 завязана на настенный час (дневной коэффициент, метки) — запуск в другой час даёт другой поток. Расцепление с фиксированной стартовой модельной точкой эту зависимость снимает.
Связь с задачей 06: персистентность (state v2 + рестарт) уже построена на настенных часах. Расцепление меняет смысл «правила 30 минут» — см. «Последствия». Эта ADR — отдельный слой поверх 06; она не правит то, что задачи 06/07 уже зафиксировали (их доводила автономная петля субагентов на реал-тайм-модели), реконсиляция идёт отдельным заходом.
Рассмотренные варианты
- Оставить ×1 (статус-кво). Отклонено: не решает учебное неудобство; историческую глубину даёт только сид, медленные явления вживую не показать.
- Живой всегда ×1, «быстро» только как разовая заливка прошлого. Жизнеспособно, но слабее: урок 7 не может вживую ускорить наблюдение, и это два разных механизма (живой стенд и батч-заливка) вместо одного.
- Живой масштабируется ×K, дефолт ×1; заливка =
K → ∞(принято). Один механизм поглощает все режимы; ускорение прямо служит уроку 7; дефолт ×1 сохраняет «дыхание» настоящего сайта; пре-заливка прошлого тоже доступна (бакаешь на огромномK, живёшь на умеренном). Цена — метки уходят вперёд настенных часов при разгоне (безвредно для аналитики).
Последствия
-
Режимы живой / ускоренный / заливка — конфигурация одного драйвера часов; реализация и имена параметров — за исполнителем.
-
Правило 30 минут (задача 06) переопределяется в модельном времени. Формула не меняется: разрыв между модельной меткой следующего события визита и модельным временем возобновления больше 30 минут → визит закрывается; меньше → продолжается. Меняется origin возобновления — он становится параметром:
- восстановление после краха — origin = настенное
now()(время реально прошло) → поведение как сегодня; - продолжение из заморозки/сида — origin = метка снимка → разрыв ≈ 0, мир продолжается непрерывно, активные визиты не закрываются.
Текущий код 06 берёт
now()жёстко: корректно для краха, требует параметризации для продолжения. - восстановление после краха — origin = настенное
-
Будущее направление (ещё не строим): «стартовая история стенда». Заливка
K → ∞+ заморозка state v2 даёт сгенерированное прошлое, с которого живой стенд стартует непрерывно (те же люди возвращаются —users < sessionsрождается вживую, без костыля статического сидаusers == sessions). При этом роль статического сида со временем смещается с «учебный датасет» на «dev-фикстура и калибровочный эталон» (термины — вCONTEXT.md). На время разработки сид и поток сосуществуют (как зафиксировано в ADR-0004); продолжение-из-сида — отдельная будущая задача после готовности генератора. -
Воспроизводимость улучшается при фиксированной стартовой модельной точке; реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки — отдельный заход после ревью эксперимента с автономной петлёй.