docs(generator): переписана задача 13 и заведён баг флаки-гейта
- Зачем:
- рамка задачи 13 пересмотрена: ценность доливки — кормление стенда
дневными батчами (урок и имитация жизни), а не удлинение истории;
- прогон пути менти 2026-07-07 показал, что runtime-гейт стыка
backfill->live нестабилен — красный/зелёный через раз.
- Что:
- задача 13 переписана под глагол next-day в generator_control:
сценарии, развилка реализации (срез артефакта против доливки от
слепка), закрыт вопрос про импорт частями (сейчас всё или ничего);
- добавлена задача 20 про нестабильную runtime-проверку стыка с
эмпирикой двух прогонов и гипотезами для диагноза.
- Проверка:
- два прогона make generated-history-runtime-check (красный 8/19,
затем зелёный) — зафиксированы в задаче 20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+76
-29
@@ -1,6 +1,6 @@
|
||||
Status: needs-triage
|
||||
Status: needs-info
|
||||
|
||||
# Доливка стартовой истории кусочком от слепка
|
||||
# Глагол next-day: собрать следующий модельный день
|
||||
|
||||
## Parent
|
||||
|
||||
@@ -8,40 +8,87 @@ Status: needs-triage
|
||||
|
||||
## Why
|
||||
|
||||
Сейчас backfill всегда стартует с чистого состояния от `T0`
|
||||
(`service.py:178-185`): удлинить существующую историю нельзя — есть 2 суток,
|
||||
нужно 7, генерируй все 7 заново. Идея — «долить» продолжение от сохранённого
|
||||
слепка `T_end`.
|
||||
Рамка пересмотрена 2026-07-07 (обсуждение с пользователем). Было: «удлинить
|
||||
готовую историю» (2 суток -> 7) — этот мотив закрыл портативный артефакт
|
||||
(задача 07), приоритет был низкий. Стало: **режим кормления стенда порциями**.
|
||||
Ценность не в длине истории, а в дискретности подачи:
|
||||
|
||||
Приоритет низкий (решение 2026-07-04): с портативным артефактом
|
||||
(задача 07) длинную историю можно сгенерировать один раз и переиспользовать,
|
||||
так что потребность в доливке может и не возникнуть. Вернуться, если возникнет.
|
||||
- **Урок.** Менти триггерит «следующий день» -> в Kafka падает батч за новый
|
||||
модельный день -> прогон `etl_pipeline` -> витрины и дашборды сдвинулись.
|
||||
Полный цикл DWH виден за один шаг; повторяемо и управляемо, в отличие от
|
||||
непрерывного live.
|
||||
- **Имитация жизни.** Тот же механизм по расписанию (`@daily` или чаще) —
|
||||
стенд «живёт»: мониторинги бегут, метрики копятся, нагрузка видна.
|
||||
- Удлинение истории остаётся как частный случай (несколько next-day подряд).
|
||||
|
||||
## What to build
|
||||
## Интерфейс
|
||||
|
||||
- Режим backfill, продолжающий от слепка: новая порция истории от `T_end` до
|
||||
нового `T_end'`, манифест обновляется.
|
||||
Новый глагол `next-day` в существующем DAG `generator_control`
|
||||
(`airflow/dags/generator_control_dag.py`), не отдельный DAG: менти уже знает,
|
||||
где ручки генератора; pause-check `etl_pipeline` и guard чистого стенда
|
||||
переиспользуются. Ручной триггер — для урока, расписание — для «жизни».
|
||||
|
||||
## Развилка реализации (решить до реализации)
|
||||
|
||||
Интерфейс `next-day` одинаков для двух начинок, начинку можно менять, не
|
||||
трогая то, чему учим:
|
||||
|
||||
1. **Резать готовый артефакт на дневные порции.** История сгенерирована одним
|
||||
прогоном (задача 07), швов нет вообще — границы дней внутри одного мира
|
||||
естественные. Дёшево, если механизм импорта умеет подавать историю частями
|
||||
(см. «Открытые вопросы»). Ограничение: горизонт равен длине артефакта.
|
||||
2. **Доливка от слепка `T_end`.** Настоящая генерация продолжения. Нужна,
|
||||
когда жизнь стенда должна быть неограниченной. Каждый батч добавляет шов;
|
||||
restore-механизм общий с рестартом и закалён задачей 09, но открыт хвост
|
||||
про запасную ветку рождения визита (см. критерии).
|
||||
|
||||
Разумный путь: вариант 1 как MVP, вариант 2 — когда упрёмся в конец артефакта
|
||||
(возможно, отдельной задачей).
|
||||
|
||||
## Открытые вопросы (блокируют ready-for-agent)
|
||||
|
||||
- [x] Умеет ли текущий импорт артефакта (`startup-history-import`) подавать
|
||||
историю частями? **Нет, «всё или ничего»** (проверено 2026-07-07):
|
||||
скрипт требует чистый стенд (`import_startup_history_artifact.sh:50`,
|
||||
`assert_stand_clean.sh`), CLI артефакта воспроизводит все топики целиком и
|
||||
не имеет опций среза, а валидация жёстко связывает manifest и state
|
||||
(`startup_history_artifact.py`, `_validate_manifest_state`). Для варианта 1
|
||||
нужен новый режим импорта: срез по модельному дню, цепочка manifest'ов
|
||||
вместо «manifest равен state», и ослабление guard'а чистого стенда для
|
||||
последующих порций (граница по манифесту вместо пустоты). Нюанс: артефакт
|
||||
хранит один state на конец полной истории, поэтому live-продолжение
|
||||
возможно только после последней порции; промежуточные дни — batch-only
|
||||
(для учебного сценария этого достаточно).
|
||||
- [ ] Для варианта 2 — решение по запасной ветке рождения визита
|
||||
(`generation.py`, ветка без донора-кандидата): либо закрепить проверкой,
|
||||
что она не срабатывает, либо ограничить длину визита так, чтобы fallback
|
||||
не менял фактуру на стыке. Унаследовано из прежней редакции задачи.
|
||||
|
||||
## Acceptance criteria (черновые)
|
||||
|
||||
- [ ] Доливка продолжает мир слепка: без дублей и смешения на границе.
|
||||
- [ ] Однородность визитов, переживших границу доливки, — включая per-event
|
||||
браузерные поля (`dds.event`), см. дефект
|
||||
`09-seam-browser-fixture-not-preserved.md`: доливка создаёт новые стыки,
|
||||
тот же механизм восстановления.
|
||||
- [ ] Манифест после доливки отражает новый `T_end'`; антисмешивание работает.
|
||||
- [ ] До реализации доливки принято решение по fallback-ветке восстановления
|
||||
визита: либо свойство донорского сида закреплено проверкой, либо длина визита
|
||||
ограничена так, чтобы fallback не менял фактуру на стыке.
|
||||
- [ ] Глагол `next-day` в `generator_control`: батч ровно за один следующий
|
||||
модельный день от текущей границы манифеста.
|
||||
- [ ] Идемпотентность: повторный триггер того же дня — громкий отказ по
|
||||
манифесту, а не второй мир и не дубли.
|
||||
- [ ] Манифест двигает `T_end` атомарно; антисмешивание работает на **цепочке**
|
||||
границ (30 батчей = 30 границ), проверки
|
||||
(`scripts/check_generated_analytics.sh` и родня) расширены с одной границы
|
||||
на цепочку.
|
||||
- [ ] Учебный цикл целиком: после `next-day` прогон `etl_pipeline` даёт новые
|
||||
строки в DM-витринах; проверка повторяемая, не разовая.
|
||||
- [ ] Для варианта 2: визиты, пережившие границу батча, однородны по всем
|
||||
per-event полям (браузер, referer, utm) — тот же класс проверки, что в
|
||||
задаче 09.
|
||||
|
||||
## Blocked by
|
||||
|
||||
- `15-world-boundary-after-cross-review.md` — доливка продолжает мир от слепка;
|
||||
сначала нужен закрытый инвариант «старый state и консольные пути не создают
|
||||
второй мир».
|
||||
- `17-trusted-checks-startup-history-superset.md` — доливка создаёт новые
|
||||
стыки; сначала нужна доверенная проверка фактической границы по манифесту.
|
||||
- Открытые вопросы выше (статус needs-info до их закрытия).
|
||||
- Исторические блокеры закрыты: `15-world-boundary-after-cross-review.md`
|
||||
(граница миров) и `17-trusted-checks-startup-history-superset.md`
|
||||
(доверенные проверки) — done.
|
||||
|
||||
Исторический контекст: `09-seam-browser-fixture-not-preserved.md` закрыла
|
||||
фактуру визита на стыке восстановления, но кросс-линейное ревью 2026-07-05
|
||||
нашло более широкие риски вокруг границы мира и проверки стыка.
|
||||
Исторический контекст: `09-seam-browser-fixture-not-preserved.md` — шов
|
||||
рождается при любом прохождении мира через сериализованный state (рестарт,
|
||||
сбой, доливка — один код восстановления); фикс 09 положил донора фактуры в
|
||||
state и закрыл тихие fallback'и. Прежняя редакция этой задачи называлась
|
||||
«Доливка стартовой истории кусочком от слепка».
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
Status: needs-triage
|
||||
|
||||
# Runtime-проверка стыка нестабильна: фактура меняется через раз
|
||||
|
||||
## Parent
|
||||
|
||||
`.scratch/generator-model-time-startup-history/PRD.md`
|
||||
|
||||
## Что нашли
|
||||
|
||||
Два подряд прогона `make generated-history-runtime-check` на одном и том же
|
||||
стенде (2026-07-07, проверка пути менти) дали разный результат:
|
||||
|
||||
- Прогон 1 (20:18–20:20): **красный** —
|
||||
`Ошибка: per-event фактура меняется на стыке: 8/19`
|
||||
(у 8 из 19 визитов, переживших границу backfill->live, поменялась
|
||||
per-event фактура). При этом `duplicate_events=0`, events=2776.
|
||||
- Прогон 2 (21:5x, та же команда, без изменений кода): **зелёный** —
|
||||
«runtime-проверка startup-history/live seam прошла», конфликтов 0,
|
||||
events=2815.
|
||||
|
||||
Конфигурация проверки: `GEN_LAUNCH_PROFILE=daily-wave`,
|
||||
`GEN_HISTORY_DURATION=1h`, `GEN_MODEL_T_END=2026-01-01T01:00:00+00:00`
|
||||
(зашита в `scripts/run_generated_history_runtime_check.sh`).
|
||||
|
||||
## Почему это важно
|
||||
|
||||
- Это гейт, которому мы доверяем стык backfill->live (задача 17); флаки-гейт
|
||||
ничего не гарантирует: красный пугает зря, зелёный ничего не доказывает.
|
||||
- Смена фактуры на стыке — класс дефекта задачи 09, который считается
|
||||
закрытым (донор фактуры сохранён в state, тихие fallback'и заменены на
|
||||
ошибки). Либо фикс неполон, либо есть второй источник расхождения.
|
||||
- Задача 13 (глагол next-day) навешивает на этот же механизм цепочку границ —
|
||||
ей нужен доверенный, стабильный гейт.
|
||||
|
||||
## Гипотезы (проверить при диагнозе)
|
||||
|
||||
1. **Тихая запасная ветка рождения визита** (`generation.py`, ветка без
|
||||
донора-кандидата): при истории в 1 час словарь визитов маленький, донора
|
||||
с достаточным числом событий может не найтись. Задача 09 требовала
|
||||
«молча оставить как есть нельзя» — проверить, что сделал фикс.
|
||||
2. Недетерминизм live-части: реальное время тиков решает, какие визиты
|
||||
пересекут границу; сам факт «через раз» указывает на зависимость от
|
||||
wall-clock, а не от сида.
|
||||
3. Проверочный SQL: убедиться, что сравнение полей не цепляет поля,
|
||||
легитимно различающиеся (по образцу исключения `event_id` в задаче 09).
|
||||
|
||||
## Acceptance criteria (черновые)
|
||||
|
||||
- [ ] Причина расхождения 8/19 найдена и названа (код, не догадка).
|
||||
- [ ] Либо генератор починен (фактура на стыке стабильна при любом профиле),
|
||||
либо проверка исправлена (если ловила легитимные различия) — с объяснением.
|
||||
- [ ] `make generated-history-runtime-check` стабилен: N подряд прогонов
|
||||
зелёные (N >= 3), зафиксировано в задаче.
|
||||
- [ ] Если срабатывает запасная ветка рождения — она стала громкой или
|
||||
воспроизводимой при восстановлении (наследие задачи 09 и открытый вопрос
|
||||
задачи 13 закрываются согласованно).
|
||||
|
||||
## Blocked by
|
||||
|
||||
- Нет. Диагноз можно начинать сразу; стенд воспроизводит через раз.
|
||||
|
||||
Связано: `09-seam-browser-fixture-not-preserved.md` (класс дефекта и решение
|
||||
про донора), `13-backfill-top-up-from-snapshot.md` (нуждается в доверенном
|
||||
гейте на цепочке границ), `17-trusted-checks-startup-history-superset.md`
|
||||
(появление этого гейта).
|
||||
Reference in New Issue
Block a user