docs(generator): зафиксирована модель времени генератора (ADR-0005)
- Зачем:
- инвариант «время генератора ≡ реальное ×1» неудобен для учебного плана:
медленные явления (возвраты, воронка) не успеть показать на уроке, а
историческую глубину живой генератор не создаёт.
- Что:
- добавлен ADR-0005: модельные часы отвязаны от настенного времени, режимы
(живой ×1 / ускоренный ×K / заливка) — драйверы одного шва, правило 30 минут
переопределено в модельном времени; сид-продолжение помечено как будущее.
- в CONTEXT.md разведены три значения «сида» и добавлен термин модельного
времени и масштаба ×K.
- добавлен handoff с отложенным ревью петли и реконсиляцией мат-спеки.
- Проверка:
- прочитать docs/adr/0005-generator-model-clock.md и раздел «Три значения
слова сид» в CONTEXT.md; git log -1.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Handoff: ADR про модельное время генератора + отложенное ревью петли
|
||||
|
||||
Дата: 2026-06-11
|
||||
Ветка: `feature/data-generator`
|
||||
Жанр: одноразовый handoff по [ADR-0003](../../docs/adr/0003-handoffs-in-scratch.md).
|
||||
|
||||
## Зачем этот handoff
|
||||
|
||||
Сессия с Fable шла в стиле brainstorm-with-docs про **модельное время
|
||||
генератора**. Параллельно идёт эксперимент: автономная петля субагентов Кодекс
|
||||
(координатор + worker + reviewer) доделывает срез генератора (задачи 06→07).
|
||||
Эти две линии **намеренно разделены** — пересмотр времени не правит то, что петля
|
||||
строит на реал-тайм-модели. Handoff фиксирует, что уже сделано и что осталось,
|
||||
чтобы не потерять контекст в новой сессии.
|
||||
|
||||
## Что сделано в этой сессии (durable, не дублирую — см. файлы)
|
||||
|
||||
- Создан **[ADR-0005](../../docs/adr/0005-generator-model-clock.md)** «Модельные
|
||||
часы генератора, отвязанные от настенного времени». Все решения там; кратко:
|
||||
модельное время расцеплено с `now()`; режимы (живой ×1 / ускоренный ×K /
|
||||
заливка `K → ∞`) — драйверы поверх одного шва; живой стенд масштабируется ×K
|
||||
(дефолт ×1); правило 30 минут переопределяется в **модельном** времени с
|
||||
параметрическим origin возобновления; «стартовая история стенда»
|
||||
(сгенерированное прошлое + заморозка state v2) — **будущее направление, ещё не
|
||||
строим**.
|
||||
- Правки **[`CONTEXT.md`](../../CONTEXT.md)** (глоссарий): разведены три значения
|
||||
слова «сид» (`GEN_SEED` / статический сид / стартовая история стенда) и добавлен
|
||||
термин «модельное время и масштаб ×K».
|
||||
|
||||
**Эти изменения (ADR-0005 + CONTEXT.md) ещё НЕ закоммичены.** Их коммит — выход
|
||||
брейншторма пользователя, его нельзя мешать с коммитами кода от петли (06/07).
|
||||
Отдельный docs-коммит.
|
||||
|
||||
## Открытые線 для следующей сессии
|
||||
|
||||
### 1. Отложенное ревью результата автономной петли (приоритет)
|
||||
|
||||
Делать **после** того, как петля закоммитит задачу 07. Это read-only ревью.
|
||||
Измерительный лист — в памяти проекта
|
||||
`memory/ralph-loop-experiment-generator.md`. Главное:
|
||||
|
||||
- **A (главный индикатор эксперимента):** ссылочная целостность при
|
||||
`restore_state`. `_validate_v2_payload` проверяет только форму, не ссылки.
|
||||
Структурно-валидный state v2 с висячей ссылкой визит→пользователь
|
||||
(`generator/src/clickstream_generator/runtime.py:283`) или пользователь→словарь
|
||||
(`runtime.py:253/255`) проходит `from_dict_safe` и роняет `restore_state` без
|
||||
обёртки try→fresh. Прогноз: петля это **пропустит** (слепые зоны внутри одной
|
||||
линии Кодекс скоррелированы). Проверить гипотезу локально структурой с висячей
|
||||
ссылкой (как пользователь проверял оценку снимка 4.7 MB и падение на битой
|
||||
вложенности).
|
||||
- **B/D (проверено OK ранее):** кулдауны по меткам, не тикам (`runtime.py:104`);
|
||||
детерминизм компактного снимка (`_stable_event_id`, restore не жжёт ГПСЧ).
|
||||
Подтвердить, что 06→07 их не сломали.
|
||||
- **C (риск, вне скоупа 06):** всплеск досылки созревших событий с прошлыми
|
||||
метками на первом тике после рестарта — всплыл ли хоть как риск в саморевью.
|
||||
- Верхнеуровневое для 07: границы задачи, честность отметок acceptance, не
|
||||
смешаны ли 06/07, `git status`.
|
||||
|
||||
Цель ревью двойная: (1) корректность 06/07; (2) мета-вывод — **какой класс
|
||||
дефектов автономная петля систематически не видит без внешнего взгляда другой
|
||||
модели**.
|
||||
|
||||
### 2. Реконсиляция мат-спеки с ADR-0005
|
||||
|
||||
После ревью петли. Разделы «Персистентность» и «Воспроизводимость» в
|
||||
`docs/specs/2026-06-10-generator-math-model.md` написаны от настенных часов;
|
||||
привести в соответствие с ADR-0005 (модельное время, правило 30 минут в
|
||||
модельном времени). Не делать сейчас — файл смежен с тем, что петля трогала.
|
||||
|
||||
## Фон (память проекта, не дублирую)
|
||||
|
||||
- `memory/ralph-loop-experiment-generator.md` — схема Ральф-цикла + измерительный
|
||||
лист.
|
||||
- `memory/generator-full-rework.md`, `memory/fable-design-codex-implementation.md`
|
||||
— направление переработки генератора и разделение ролей Fable(дизайн)/Codex(код).
|
||||
- `.scratch/handoffs/2026-06-11-subagent-coordinator-experiment.md` — детальная
|
||||
схема эксперимента с координатором и наблюдения по циклам 05/06.
|
||||
|
||||
## Suggested skills
|
||||
|
||||
- `conventional-commits` — для отдельного docs-коммита ADR-0005 + `CONTEXT.md`
|
||||
(не мешать с кодом петли).
|
||||
- `brainstorm-with-docs` — когда вернёмся к реконсиляции спеки или к проектированию
|
||||
«стартовой истории стенда» (будущее направление из ADR-0005).
|
||||
- `handoff` — после ревью петли зафиксировать рефлексию эксперимента (что петля
|
||||
поймала/пропустила, особенно по находке A).
|
||||
+26
@@ -66,6 +66,32 @@ user_domain_id (пользователь, постоянный)
|
||||
межсессионными паузами. Именно возвраты дают расхождение `users < sessions` —
|
||||
то, чего нет на сиде (`users == sessions`) и что отличает поток от статики.
|
||||
|
||||
### Три значения слова «сид»
|
||||
|
||||
Слово перегружено — в разговоре про генератор это **три разные сущности**, их
|
||||
нельзя путать (см. [ADR-0005](./docs/adr/0005-generator-model-clock.md)):
|
||||
|
||||
- **`GEN_SEED`** — зерно ГПСЧ генератора (детерминизм случайных решений). Не
|
||||
данные, а число.
|
||||
- **статический сид** (`data/*.jsonl`) — учебный демо-датасет режима `bootstrap`
|
||||
(уроки 0–6) и **калибровочный эталон** генератора (профиль ниже — опорные
|
||||
цифры). По мере появления «стартовой истории стенда» его учебная роль смещается
|
||||
на **dev-фикстуру и эталон** (быстрый повторяемый вход для пайплайна STG→DM, не
|
||||
зависящий от генератора); смещение зафиксировано как будущее направление в
|
||||
ADR-0005, на время разработки сид и поток сосуществуют.
|
||||
- **стартовая история стенда** — сгенерированное прошлое (заливка `K → ∞` +
|
||||
заморозка состояния), с которого живой стенд стартует непрерывно. Ещё не
|
||||
строим; см. ADR-0005.
|
||||
|
||||
### Модельное время и масштаб (×K)
|
||||
|
||||
**Модельное время стенда** отвязано от настенных часов: генератор крутит
|
||||
внутренние часы, а драйвер задаёт скорость — ×1 (как реальное время), ×K
|
||||
(ускоренно, учебная «ручка» урока 7) или `K → ∞` (мгновенная заливка прошлого).
|
||||
При ускорении «сейчас» стенда уходит вперёд настенного времени — это свойство, не
|
||||
баг (ключ аналитики — `event_timestamp`). Решение и режимы —
|
||||
[ADR-0005](./docs/adr/0005-generator-model-clock.md).
|
||||
|
||||
## Почему на демо `Unique Users == Unique Sessions`
|
||||
|
||||
В демо-датасете (`data/*.jsonl`) каждый пользователь имеет **ровно один**
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# 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 и правило рестарта построены на настенных часах).
|
||||
|
||||
## Решение
|
||||
|
||||
Модельное время генератора **расцеплено** от настенных часов (`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);
|
||||
продолжение-из-сида — отдельная будущая задача после готовности генератора.
|
||||
- Воспроизводимость улучшается при фиксированной стартовой модельной точке;
|
||||
реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки —
|
||||
отдельный заход после ревью эксперимента с автономной петлёй.
|
||||
Reference in New Issue
Block a user