From 341637c8107a5322efb16ce37ea39cb11e0d034c Mon Sep 17 00:00:00 2001 From: Dmitry Dementiev Date: Thu, 11 Jun 2026 18:37:27 +0300 Subject: [PATCH] =?UTF-8?q?docs(generator):=20=D0=B7=D0=B0=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D1=81=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=20(ADR-0005)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - инвариант «время генератора ≡ реальное ×1» неудобен для учебного плана: медленные явления (возвраты, воронка) не успеть показать на уроке, а историческую глубину живой генератор не создаёт. - Что: - добавлен ADR-0005: модельные часы отвязаны от настенного времени, режимы (живой ×1 / ускоренный ×K / заливка) — драйверы одного шва, правило 30 минут переопределено в модельном времени; сид-продолжение помечено как будущее. - в CONTEXT.md разведены три значения «сида» и добавлен термин модельного времени и масштаба ×K. - добавлен handoff с отложенным ревью петли и реконсиляцией мат-спеки. - Проверка: - прочитать docs/adr/0005-generator-model-clock.md и раздел «Три значения слова сид» в CONTEXT.md; git log -1. --- ...1-generator-time-adr-and-pending-review.md | 86 ++++++++++++++ CONTEXT.md | 26 +++++ docs/adr/0005-generator-model-clock.md | 106 ++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 .scratch/handoffs/2026-06-11-generator-time-adr-and-pending-review.md create mode 100644 docs/adr/0005-generator-model-clock.md diff --git a/.scratch/handoffs/2026-06-11-generator-time-adr-and-pending-review.md b/.scratch/handoffs/2026-06-11-generator-time-adr-and-pending-review.md new file mode 100644 index 0000000..d3c5dc1 --- /dev/null +++ b/.scratch/handoffs/2026-06-11-generator-time-adr-and-pending-review.md @@ -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). diff --git a/CONTEXT.md b/CONTEXT.md index 5a24a68..408f595 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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`) каждый пользователь имеет **ровно один** diff --git a/docs/adr/0005-generator-model-clock.md b/docs/adr/0005-generator-model-clock.md new file mode 100644 index 0000000..21d736d --- /dev/null +++ b/docs/adr/0005-generator-model-clock.md @@ -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); + продолжение-из-сида — отдельная будущая задача после готовности генератора. +- Воспроизводимость улучшается при фиксированной стартовой модельной точке; + реконсиляция разделов «Персистентность» и «Воспроизводимость» мат-спеки — + отдельный заход после ревью эксперимента с автономной петлёй.