docs(generator): диагноз флаки-гейта в задаче 20, задача 13 разблокирована

- Зачем:
  - 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>
This commit is contained in:
2026-07-07 22:29:42 +03:00
co-authored by Claude Fable 5
parent 984d6fc912
commit 98be722808
2 changed files with 47 additions and 23 deletions
@@ -1,4 +1,4 @@
Status: needs-triage
Status: ready-for-agent
# Runtime-проверка стыка нестабильна: фактура меняется через раз
@@ -33,28 +33,52 @@ Status: needs-triage
- Задача 13 (глагол next-day) навешивает на этот же механизм цепочку границ —
ей нужен доверенный, стабильный гейт.
## Гипотезы (проверить при диагнозе)
## Диагноз (Codex, 2026-07-07; ключевой факт перепроверен координатором)
1. **Тихая запасная ветка рождения визита** (`generation.py`, ветка без
донора-кандидата): при истории в 1 час словарь визитов маленький, донора
с достаточным числом событий может не найтись. Задача 09 требовала
«молча оставить как есть нельзя» — проверить, что сделал фикс.
2. Недетерминизм live-части: реальное время тиков решает, какие визиты
пересекут границу; сам факт «через раз» указывает на зависимость от
wall-clock, а не от сида.
3. Проверочный SQL: убедиться, что сравнение полей не цепляет поля,
легитимно различающиеся (по образцу исключения `event_id` в задаче 09).
**Корень: гонка остановки 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».
## Acceptance criteria (черновые)
Эмпирика: в красном прогоне `browser_raw=2776`, но `location_raw=2736`
40 live-событий без пары; в зелёном `browser_raw=location_raw=2815`, поэтому
проверка прошла (хотя device/geo и там отстали: 2781).
- [ ] Причина расхождения 8/19 найдена и названа (код, не догадка).
- [ ] Либо генератор починен (фактура на стыке стабильна при любом профиле),
либо проверка исправлена (если ловила легитимные различия) — с объяснением.
Судьба гипотез:
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), зафиксировано в задаче.
- [ ] Если срабатывает запасная ветка рождения — она стала громкой или
воспроизводимой при восстановлении (наследие задачи 09 и открытый вопрос
задачи 13 закрываются согласованно).
## Blocked by