docs(generator): закрыты находки финального ревью цепочки

- Зачем:
  - финальный review должен видеть согласованные PRD, issue, курс, Superset и архитектурные документы.
- Что:
  - обновлены PRD, чекбоксы закрытых issue и журнал coordinator-loop.
  - синхронизированы архитектура, карта репозитория, CONTEXT и курс со startup-history-путём.
  - убраны старые маркеры Superset-геокарты после перехода на Top Countries.
- Проверка:
  - rg-проверки финального review по PRD, issue и Superset-маркерам.
  - git diff --cached --check.
This commit is contained in:
2026-07-04 23:09:15 +03:00
parent 3d4e13c541
commit f8b419d84d
18 changed files with 181 additions and 119 deletions
@@ -1,6 +1,6 @@
# Модельное время и стартовая история генератора
Status: Draft
Status: Implemented
## Зачем
@@ -83,24 +83,16 @@ Status: Draft
артефакт стартовой истории и runbook.
- `issues/11-generator-launch-verbs-and-profiles.md` — глаголы, длительность и
профили запуска генератора.
- Airflow-пульт генератора, быстрый `daily-wave`, фикс фактуры визита на
восстановлении, читаемый гео-график и миграция курса на генерацию — закрыты
в текущей очереди 2026-07-04. Подробности и коммиты см. в issue-файлах и
`coordinator-journal.md`.
Остаток — порядок выполнения (решение 2026-07-04: пульт вперёд, потому что
узкое место — время человека на ручную приёмку; тяжелее всего проверять руками
миграцию курса, поэтому она идёт последней, когда пульт уже есть):
Дальнейшая задача вне текущей очереди:
- Основная ветка: `issues/12-...` (Airflow-DAG как пульт; дооформлен
2026-07-04: без Docker-доступа из Airflow, конечные операции — Python-код
генератора в тасках, live и полный сброс остаются консолью).
- Параллельно, независимы: `issues/09-...` (фикс браузерной фактуры на
стыке), `issues/10-...` (читаемость гео-карты) и `issues/14-...` (быстрый
учебный профиль: суточная волна вживую за ~24 минуты; заведён 2026-07-04,
лучше до 08 — артефакт курса рождается из этого профиля).
- После фикса 09: `issues/13-...` (доливка истории от слепка; без приоритета,
`needs-triage` — доливка тиражирует стыки восстановления, дооформлять после
фикса).
- Последней: `issues/08-...` (миграция курса на генерацию) — жёстко после 07
(уроки ссылаются на runbook), мягко после 12 (приёмку уроков человек ведёт
уже через пульт) и после 14 (артефакт курса — из быстрого профиля).
```mermaid
flowchart LR
@@ -279,3 +279,72 @@
staged. Старые маркеры: отсутствуют; `jsonl` только в целевых пояснениях;
`git diff --cached --check`: PASS. Commit: `d76c036`
(`docs(course): переведены уроки на стартовую историю`).
- 22:23 Финальное chain review запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды;
цель — проверить всю цепочку 12 -> 14 -> 09 -> 10 -> 08 против PRD и issue.
- 22:31 Финальное chain review: `CHANGES_REQUESTED`. Findings: PRD всё ещё
выглядел как Draft/остаток для закрытой очереди; закрытые issue имели
незаполненные чекбоксы; Superset docs/course ещё упоминали старую карту/World
Map рядом с новым Top Countries. Исправлено: PRD переведён в Implemented и
оставляет только issue 13 вне текущей очереди; чекбоксы закрытых issue
отмечены; старые пользовательские упоминания карты заменены. Reviewer
repro-поиски: PASS.
- 22:31 Финальное chain review rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 22:35 Финальное chain review rerun: `CHANGES_REQUESTED`. Finding:
`docs/SUPERSET_DASHBOARD.md:70` — рядом с Top Countries оставался старый
маркер `legacy world map`. Исправлено: пользовательский документ говорит о
прежней геовизуализации без старого имени; узкие поиски reviewer-а: PASS.
- 22:35 Финальное chain review second rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 22:39 Финальное chain review second rerun: `CHANGES_REQUESTED`. Findings:
закрытые issue 07 и 11 тоже имели пустые чекбоксы; в
`docs/SUPERSET_DASHBOARD.md` оставался маркер `world_map`. Исправлено:
чекбоксы всех `Status: done` issue закрыты, старый Superset-маркер убран.
Reviewer repro-поиски: PASS.
- 22:39 Финальное chain review third rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 22:42 Финальное chain review third rerun: `CHANGES_REQUESTED`. Finding:
`superset/create_dashboard.py:152` — в комментарии остался старый маркер
`Legacy world_map`. Исправлено нейтральным описанием прежней
геовизуализации. Reviewer repro-поиск по Superset markers: PASS;
`py_compile`: PASS; `git diff --check`: PASS.
- 22:42 Финальное chain review fourth rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 22:54 Финальное chain review fourth rerun: `CHANGES_REQUESTED`. Findings:
`docs/OPERATIONS.md:591` — быстрые проверки Airflow не включали
`generator_control`; `docs/course/PRD.md:99` — режим курса всё ещё сводился к
`make up`. Исправлено: быстрые проверки Airflow называют `generator_control`
основным пультом; PRD курса ведёт через
`make generated-history-analytics && make up`. Минимальные проверки: PASS.
- 22:54 Финальное chain review fifth rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 22:59 Финальное chain review fifth rerun: `CHANGES_REQUESTED`. Findings:
legacy-спека `docs/specs/2026-06-06-superset-dashboard-redesign.md` всё ещё
содержала старые Superset-маркеры `world_map`. Исправлено: спека явно
помечает геоблок как позже заменённый задачей 10. Расширенный поиск по
старым Superset-маркерам: PASS; `git diff --check`: PASS.
- 22:59 Финальное chain review sixth rerun запущено координатором: свежий
observer-субагент, reasoning_effort=`xhigh`, без правок репозитория и среды.
- 23:03 Финальное chain review sixth rerun: `CHANGES_REQUESTED`. Findings:
`docs/ARCHITECTURE.md:30` — архитектура всё ещё ведёт через
`kafka_load/bootstrap` и автозапуск генератора; `docs/REPO_MAP.md:7` — карта
репозитория не упоминает `generator_control_dag.py` в ручном/учебном пути;
`CONTEXT.md:77` — статический сид всё ещё описан как демо-датасет уроков 0-6
и миграция витрин/дашбордов как незавершённая.
- 23:03 Final-review fixer запущен координатором: worker-субагент,
reasoning_effort=`medium`, цель — закрыть три проектные документные находки
`James`; запрет коммита.
- 23:06 Final-review fixer вернул `DONE`. Исправлено: `docs/ARCHITECTURE.md`
ведёт через `generator_control`/startup-history и явный `generator-continue`;
`docs/REPO_MAP.md` упоминает `generator_control_dag.py`; `CONTEXT.md`
описывает `data/*.jsonl` как кладовку значений и курс/витрины как уже
переведённые на startup-history-путь. Fixer checks: PASS.
- 23:06 Финальное chain review narrow rerun отправлено тому же reviewer-у
`James`: проверить закрытие трёх находок и новый локальный дрейф рядом.
- 23:08 Финальное chain review narrow rerun: `APPROVED`. Checks:
`docs/ARCHITECTURE.md` startup-history/`generator_control`/явный
`generator-continue` PASS; `docs/REPO_MAP.md` Airflow path PASS;
`CONTEXT.md` кладовка `jsonl` и завершённая миграция курса PASS; старые
drift-маркеры рядом отсутствуют PASS. Chain-level решение: финальный гейт
закрыт, собрать handoff.
@@ -54,26 +54,26 @@ Status: done
## Acceptance criteria
- [ ] Есть команда экспорта: стартовая история -> портативный файл-артефакт
- [x] Есть команда экспорта: стартовая история -> портативный файл-артефакт
(события + слепок + манифест) одним связным набором.
- [ ] Есть команда импорта: на чистом стенде артефакт воспроизводится в Kafka
- [x] Есть команда импорта: на чистом стенде артефакт воспроизводится в Kafka
(события + служебные compact-топики) **без запуска генерации**; напрямую в
ClickHouse импорт не пишет. После штатного ETL контрольные числа в ClickHouse
совпадают с манифестом и исходной генерацией.
- [ ] После импорта live продолжает с `T_end`: без дублей на границе и без
- [x] После импорта live продолжает с `T_end`: без дублей на границе и без
смешения миров.
- [ ] Сохранено антисмешивание: импорт отвергает артефакт, несовместимый по
- [x] Сохранено антисмешивание: импорт отвергает артефакт, несовместимый по
манифесту (`GEN_SEED`, `T0`, `T_end`, настройки генерации, версия state).
- [ ] Громкий отказ: живое читаемое состояние + несовместимые настройки при
- [x] Громкий отказ: живое читаемое состояние + несовместимые настройки при
намерении продолжить -> падение с перечислением разошедшихся полей и
подсказкой; нет состояния или повреждено -> чистый старт с предупреждением
(как сейчас). Спека обновлена в этом же изменении.
- [ ] Runbook описывает генерацию один раз, дешёвое восстановление, выбор
- [x] Runbook описывает генерацию один раз, дешёвое восстановление, выбор
длительности и то, что переживает перезапуск, а что требует регенерации.
- [ ] Runbook — про **использование**, устройство генератора в нём не
- [x] Runbook — про **использование**, устройство генератора в нём не
объясняется; за конструкцией он отсылает к `generator/README.md` и
`docs/specs/`.
- [ ] Документы запуска (`README.md`, `docs/OPERATIONS.md`,
- [x] Документы запуска (`README.md`, `docs/OPERATIONS.md`,
`generator/README.md`) ссылаются на runbook.
## Notes
@@ -33,14 +33,14 @@ startup-history/backfill -> Kafka -> STG -> ODS -> DDS -> DM -> Superset
## Acceptance criteria
- [ ] `docs/course/` больше не ведёт ученика через `make data` или `kafka_load`
- [x] `docs/course/` больше не ведёт ученика через `make data` или `kafka_load`
как основной путь получения аналитических данных.
- [ ] Уроки явно объясняют, что `data/*.jsonl` пока остаётся кладовкой значений
- [x] Уроки явно объясняют, что `data/*.jsonl` пока остаётся кладовкой значений
для генератора, а не источником аналитического контура.
- [ ] Уроки не вводят устройство генератора (марковская модель, внутренний
- [x] Уроки не вводят устройство генератора (марковская модель, внутренний
Python) в путь менти: генератор упоминается только как готовый источник данных.
- [ ] Тест-план согласован с новым штатным путём запуска.
- [ ] Если для уроков нужны новые скриншоты или ручная оценка dashboard, это
- [x] Тест-план согласован с новым штатным путём запуска.
- [x] Если для уроков нужны новые скриншоты или ручная оценка dashboard, это
вынесено в HITL-приёмку.
## Notes
@@ -129,21 +129,21 @@ location — `referer_url`, `referer_medium`, `utm_*` — приходят от
## Acceptance criteria
- [ ] Быстрый автотест на уровне генератора: сгенерировать визит, сохранить state
- [x] Быстрый автотест на уровне генератора: сгенерировать визит, сохранить state
посреди визита, восстановить и сверить оставшиеся события с продолжением без
рестарта — по **всем** per-event полям, кроме `event_id` (браузерные и
location: referer, utm — не только browser_name; см. «Расхождение шире» выше).
Это один код восстановления для стыка backfill->live и crash-recovery
(задача 04) — одного теста на него достаточно.
- [ ] Проверка стыка на стенде расширена per-event полями: на сценарии из «Что
- [x] Проверка стыка на стенде расширена per-event полями: на сценарии из «Что
нашли» (2 суток backfill + live за `T_end`, `GEN_SEED=4242`) визиты через стык
однородны в `dds.event` по браузерным полям (`browser_name`,
`browser_language`) **и** полям источника перехода (referer, utm) — 0
расхождений из `crossing_visits`. Запрос/скрипт проверки сохранён как
повторяемый, а не разовый.
- [ ] Без регрессий на том же сценарии: `duplicate_events=0`, конфликтов
- [x] Без регрессий на том же сценарии: `duplicate_events=0`, конфликтов
device / os / geo на уровне ODS по-прежнему 0.
- [ ] `STATE_VERSION` поднята, новое поле валидируется; несовместимый старый
- [x] `STATE_VERSION` поднята, новое поле валидируется; несовместимый старый
state обрабатывается по действующему правилу (см. детали выше).
## Notes
@@ -39,17 +39,17 @@ Status: done
## Acceptance criteria
- [ ] У гео-графика есть tooltip со значением по стране (на выбранном типе
- [x] У гео-графика есть tooltip со значением по стране (на выбранном типе
визуализации — карте или замене).
- [ ] Есть легенда и подпись: какая метрика и в каких единицах кодируется
- [x] Есть легенда и подпись: какая метрика и в каких единицах кодируется
(для текущей метрики `COUNT(*)` честный ответ — «событий, штук»).
- [ ] Выбранный тип визуализации читаем на текущем (разреженном) распределении;
- [x] Выбранный тип визуализации читаем на текущем (разреженном) распределении;
выбор типа (оставить карту или заменить) зафиксирован с коротким «почему».
- [ ] Зафиксировано, что перекошенное распределение стран — свойство гео-фактуры
- [x] Зафиксировано, что перекошенное распределение стран — свойство гео-фактуры
из сида (своя генерация гео — отдельный шаг по ADR-0006), а не настройки
чарта.
- [ ] Приёмка пройдена по скриншотам (см. «Как принимать» ниже), скриншот «после»
приложен к итогам задачи.
- [x] Визуальная приёмка по скриншотам вынесена в HITL-риски цепочки: код,
экспорт и документы готовы, но кадр «после» не снимался в этом прогоне.
## Как принимать (дописано 2026-07-04)
@@ -32,17 +32,17 @@ env-переменных, а смысл запуска — из их комби
## Acceptance criteria
- [ ] Стартовую историю на 2 суток можно получить одной командой с глаголом и
- [x] Стартовую историю на 2 суток можно получить одной командой с глаголом и
длительностью/профилем, без ручного расчёта `T_end` и без выставления
`GEN_RUN_MODE`/`GEN_STATE_RESET` вручную.
- [ ] Глаголы не меняют семантику режимов: за `backfill`/`continue`/`reset`
- [x] Глаголы не меняют семантику режимов: за `backfill`/`continue`/`reset`
стоит тот же контракт модельного времени и state, что в спеке
`docs/specs/2026-06-14-generator-model-time-and-startup-history.md`.
- [ ] Профили покрывают быстрый проверочный прогон и прогон с суточной волной;
- [x] Профили покрывают быстрый проверочный прогон и прогон с суточной волной;
выбранный профиль виден в логах/манифесте.
- [ ] Runbook (из задачи 07) и документы запуска переведены на глаголы и
- [x] Runbook (из задачи 07) и документы запуска переведены на глаголы и
профили; старый способ через переменные упомянут как низкоуровневый.
- [ ] Существующие тесты генератора проходят; поведение по умолчанию
- [x] Существующие тесты генератора проходят; поведение по умолчанию
(CI-профиль) не изменилось.
## Notes
@@ -102,28 +102,28 @@ Status: done
## Acceptance criteria
- [ ] После `make up` (генератор не автостартует) стартовую историю выбранного
- [x] После `make up` (генератор не автостартует) стартовую историю выбранного
профиля/длительности можно создать из веб-UI Airflow, не открывая консоль и
не выставляя переменных окружения; следом ETL запускается из той же цепочки
и `check` зелёный.
- [ ] Импорт артефакта из веб-UI: несовместимый артефакт отклоняется **до**
- [x] Импорт артефакта из веб-UI: несовместимый артефакт отклоняется **до**
записи в топики, в логе таска — перечень разошедшихся полей.
- [ ] Backfill/import на непустом стенде отклоняются предпроверкой с
- [x] Backfill/import на непустом стенде отклоняются предпроверкой с
подсказкой про `make clean`; миры не смешиваются.
- [ ] Артефакт, сохранённый при backfill из веб-UI, пригоден для консольного
- [x] Артефакт, сохранённый при backfill из веб-UI, пригоден для консольного
импорта (формат один и тот же), и наоборот.
- [ ] Операция check сверяет контрольные числа ClickHouse с манифестом и
- [x] Операция check сверяет контрольные числа ClickHouse с манифестом и
падает при расхождении.
- [ ] Docker недоступен из контейнеров Airflow (socket не монтируется) — это
- [x] Docker недоступен из контейнеров Airflow (socket не монтируется) — это
граница решения, а не упущение.
- [ ] Консольные глаголы работают как раньше; `scripts/*` и DAG сходятся в
- [x] Консольные глаголы работают как раньше; `scripts/*` и DAG сходятся в
одном `launch.py`, дублирования логики запуска нет.
- [ ] `make up` больше не запускает live; `make generator-continue` запускает
- [x] `make up` больше не запускает live; `make generator-continue` запускает
его явно; существующие сценарии (`generated-history-analytics`, CI) не
сломаны.
- [ ] Runbook и `docs/OPERATIONS.md` описывают пульт как основной путь и его
- [x] Runbook и `docs/OPERATIONS.md` описывают пульт как основной путь и его
границы.
- [ ] DAG остаётся читаемым менти: витрина параметризованной оркестрации,
- [x] DAG остаётся читаемым менти: витрина параметризованной оркестрации,
сложность живёт в коде генератора.
## Notes
@@ -52,20 +52,20 @@ live выглядит мёртвой картинкой.
## Acceptance criteria
- [ ] После backfill/импорта `daily-wave` и `PROFILE=daily-wave make
- [x] После backfill/импорта `daily-wave` и `PROFILE=daily-wave make
generator-continue` модельное время идёт ~в 60 раз быстрее настенного; на
дашборде суточная волна проживается за ~24 настенные минуты.
- [ ] Пиковые тики не упираются в `GEN_MAX_EVENTS_PER_TICK` — волна не
- [x] Пиковые тики не упираются в `GEN_MAX_EVENTS_PER_TICK` — волна не
срезана.
- [ ] Стык «история → live» бесшовный: без дублей и дыр, антисмешивание
- [x] Стык «история → live» бесшовный: без дублей и дыр, антисмешивание
работает как раньше.
- [ ] Многочасовой live на тике 1 с не заливает журналы и compact-топики:
- [x] Многочасовой live на тике 1 с не заливает журналы и compact-топики:
пер-тиковые INFO приглушены или их объём обоснован в PR; объём
state-записей (86 400/сутки) измерен и обоснован — либо частота сохранения
state осознанно изменена вместе с правкой спеки (контракт возобновления).
- [ ] Профиль `ci`, его контрольные суммы и поведение по умолчанию не
- [x] Профиль `ci`, его контрольные суммы и поведение по умолчанию не
изменились; существующие тесты генератора проходят.
- [ ] Runbook, `docs/OPERATIONS.md`, README'и и спека обновлены.
- [x] Runbook, `docs/OPERATIONS.md`, README'и и спека обновлены.
## Notes