- Зачем:
- Codex-диагноз нашёл корень нестабильности runtime-гейта: гонка
остановки live-генератора, batch публикуется по топикам без
атомарности, seam-SQL маскирует непарные строки под смену фактуры;
- попутно закрыт второй открытый вопрос задачи 13 - запасная ветка
рождения визита уже хранит донора (фикс задачи 09).
- Что:
- задача 20: диагноз с судьбой трёх гипотез, направление фикса
(управляемая остановка live + precheck непарных строк), статус
ready-for-agent;
- задача 13: вопрос про fallback-ветку закрыт, статус ready-for-human
(осталось решение по развилке реализации).
- Проверка:
- утверждение про донора перепроверено по generation.py:194-209;
эмпирика диагноза - в логах прогонов, пути в задаче 20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
91 lines
6.1 KiB
Markdown
91 lines
6.1 KiB
Markdown
Status: ready-for-agent
|
||
|
||
# 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) навешивает на этот же механизм цепочку границ —
|
||
ей нужен доверенный, стабильный гейт.
|
||
|
||
## Диагноз (Codex, 2026-07-07; ключевой факт перепроверен координатором)
|
||
|
||
**Корень: гонка остановки live-генератора, а не смена фактуры.** Цепочка:
|
||
`run_generated_history_runtime_check.sh` останавливает live сразу после первых
|
||
новых STG-строк -> генератор публикует batch по четырём топикам
|
||
последовательно, без атомарности -> `docker compose stop generator` может
|
||
оборвать процесс между топиками -> часть browser-событий остаётся без парных
|
||
location/device/geo -> DDS строит `dds.event` через `LEFT JOIN location`,
|
||
и такие строки получают `NULL` в referer/utm -> seam-SQL считает это «сменой
|
||
фактуры внутри click_id».
|
||
|
||
Эмпирика: в красном прогоне `browser_raw=2776`, но `location_raw=2736` —
|
||
40 live-событий без пары; в зелёном `browser_raw=location_raw=2815`, поэтому
|
||
проверка прошла (хотя device/geo и там отстали: 2781).
|
||
|
||
Судьба гипотез:
|
||
|
||
1. Запасная ветка рождения — **опровергнута**: ветка уже хранит донора
|
||
(`generation.py:194-209`, комментарий «Запасная ветка тоже восстановима»)
|
||
и громко падает на неполных locations. Перепроверено координатором по коду.
|
||
2. Недетерминизм live — **подтверждена частично**: решает не сид, а настенный
|
||
момент остановки процесса и какие топики успели дописаться.
|
||
3. Проверочный SQL — **опровергнута в формулировке**: поля не «легитимно
|
||
различаются», SQL маскирует пропущенный location под «смену фактуры».
|
||
|
||
## Направление фикса (из диагноза)
|
||
|
||
1. **Основное:** сделать остановку live управляемой — завершать после полного
|
||
batch/tick и flush всех топиков (bounded live mode или ожидание записи
|
||
`generator_batch_history` с полными sent-счётчиками).
|
||
2. **Дополнительно:** precheck в seam-check на непарные
|
||
browser/location/device/geo live-строки — чтобы ошибка называла реальную
|
||
причину, а не «фактура поменялась».
|
||
3. Просто увеличить sleep перед stop — отклонено: снижает вероятность,
|
||
но гонку не убирает.
|
||
|
||
## Acceptance criteria
|
||
|
||
- [x] Причина расхождения 8/19 найдена и названа (код, не догадка) —
|
||
см. «Диагноз» выше.
|
||
- [ ] Остановка live в runtime-check управляемая: batch дописывается во все
|
||
топики целиком до остановки (направление фикса, пункт 1).
|
||
- [ ] Seam-check различает «непарные live-строки» и «смена фактуры»:
|
||
precheck называет реальную причину (пункт 2).
|
||
- [ ] `make generated-history-runtime-check` стабилен: N подряд прогонов
|
||
зелёные (N >= 3), зафиксировано в задаче.
|
||
|
||
## Blocked by
|
||
|
||
- Нет. Диагноз можно начинать сразу; стенд воспроизводит через раз.
|
||
|
||
Связано: `09-seam-browser-fixture-not-preserved.md` (класс дефекта и решение
|
||
про донора), `13-backfill-top-up-from-snapshot.md` (нуждается в доверенном
|
||
гейте на цепочке границ), `17-trusted-checks-startup-history-superset.md`
|
||
(появление этого гейта).
|