Files
ddadminandClaude Opus 4.8 49512b190a docs(generator): зафиксированы источник аналитики и стартовая история
- Зачем:
  - генератор даёт здоровую пирамиду, и стенду нужен единый источник аналитики вместо вырожденного статического сида.
- Что:
  - добавлен ADR-0006: генерация — единственный источник аналитики, статический сид становится архивным (кладовка значений до синтеза фактуры).
  - добавлена спека модельного времени: точка отсчёта, заливка прошлого, стартовая история, сохранение состояния, воспроизводимость и проверка в два шага.
  - в ADR-0004 и ADR-0005 добавлены указатели вперёд на ADR-0006 и спеку.
- Проверка:
  - чтение документов; перекрёстные ссылки между ADR-0004/0005/0006 и спекой согласованы.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:47:20 +03:00

112 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0005: Модельные часы генератора, отвязанные от настенного времени
Принято: 2026-06-11
Статус: accepted
Связано: [ADR-0004](./0004-steady-stream-synthetic-generator.md) (расширяет —
steady-stream как синтетический генератор), [`CONTEXT.md`](../../CONTEXT.md)
(термины времени и «сида»), мат-спека
[`docs/specs/2026-06-10-generator-math-model.md`](../specs/2026-06-10-generator-math-model.md)
(разделы «Персистентность» и «Воспроизводимость» написаны от настенных часов —
будут реконсилированы отдельным заходом), задача
[`06-state-v2-and-restart`](../../.scratch/feature-data-generator/issues/06-state-v2-and-restart.md)
(state v2 и правило рестарта построены на настенных часах).
> Обновление (2026-06-14): направление «стартовая история стенда» запущено в
> [ADR-0006](./0006-generation-as-sole-analytics-source.md); реконсиляция
> разделов «Персистентность» и «Воспроизводимость» мат-спеки выполнена в
> [спеке модельного времени](../specs/2026-06-14-generator-model-time-and-startup-history.md).
## Решение
Модельное время генератора **расцеплено** от настенных часов (`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()` жёстко: корректно для краха, требует
параметризации для продолжения.
- **Будущее направление (ещё не строим): «стартовая история стенда».** Заливка
`K → ∞` + заморозка state v2 даёт сгенерированное прошлое, с которого живой
стенд стартует **непрерывно** (те же люди возвращаются — `users < sessions`
рождается вживую, без костыля статического сида `users == sessions`). При этом
роль статического сида со временем смещается с «учебный датасет» на
«dev-фикстура и калибровочный эталон» (термины — в `CONTEXT.md`). **На время
разработки** сид и поток **сосуществуют** (как зафиксировано в ADR-0004);
продолжение-из-сида — отдельная будущая задача после готовности генератора.
- Воспроизводимость улучшается при фиксированной стартовой модельной точке;
реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки —
отдельный заход после ревью эксперимента с автономной петлёй.