Files
clickstream-ch-kafka-supers…/.scratch/handoffs/20260705-0002-cross-review-generator-chain.md
T
ddadminandClaude Fable 5 749ae91a07 docs(handoff): кросс-линейное ревью цепочки генератора
- Зачем:
  - зафиксировать результат кросс-прохода другой родословной по
    c672ed0..ec815ce для передачи в багфикс-цепочку.
- Что:
  - отчёт: 1 critical, 5 high, 10 medium, 4 low; четыре сквозных семейства
    (смешение миров, стенд без live, ложнозелёные проверки, чистый клон).
  - маршрутизация находок и предложение порядка багфикс-цепочки.
- Проверка:
  - находки доказаны исполнением: мутационные тесты, compose-эксперимент,
    зонды на сиде, Context7 по Airflow 2.10.5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 00:03:44 +03:00

161 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Кросс-линейное ревью цепочки генератора 12 -> 14 -> 09 -> 10 -> 08
Дата: 2026-07-05 00:02.
Жанр: одноразовый handoff по ADR-0003; отчёт кросс-прохода другой родословной
(Claude) по диапазону `c672ed0..ec815ce`. Бриф — из
`.scratch/handoffs/2026-07-04-generator-chain-review.md`.
Метод: четыре параллельных адверсарных ревьюера с исполняемыми доказательствами
(мутационные тесты на копии, эксперимент с compose-профилем на реальной версии
Compose, зонды на реальном сиде, сверка Airflow API через Context7). Репозиторий
не менялся.
## Вердикт
**CHANGES_REQUESTED.** Счёт: 1 critical, 5 high, 10 medium, 4 low — после
`APPROVED` финального ревью той же линии. Третье подтверждение находки о
родословной: внутренние ревью закрыли локальные дефекты честно, кросс-линия
нашла сквозные сценарии.
## Сквозные семейства (главная ценность прохода)
1. **«Смешение миров» возвращается тремя дорогами.** Инвариант задачи 07
(«чистый стенд, громкий отказ») пробит: (а) state v2 после обновления кода
классифицируется как «повреждённый» → тихий чистый старт нового мира поверх
истории; (б) live-генератор ушёл в compose-профиль, и `make clean`/`down`
его больше **не убивают** — осиротевший генератор старого мира пишет в чистые
топики и переживает перезагрузку (`restart: unless-stopped`); (в) консольные
пути (`make generator-backfill`, `startup-history-import`) остались без
предпроверок, которые пульту сочли обязательными.
2. **Последствия «make up больше не стартует live» не выметены.** Алерт
`Kafka No Messages Produced` горит весь курс на здоровом стенде; уроки 0/5
ожидают поток; `sleep 5` в документированной проверке шва меньше времени
старта генератора — проверка стала пустышкой.
3. **Ложнозелёные проверки.** Superset-тесты пропускают дрейф параметров
(top-15 → 500 строк — 5 тестов зелёные); стыковая проверка сверяет шов там,
куда указал env, а не где он по манифесту; ODS-блок проходит на пустом ODS.
4. **Чистый клон/свежий стенд никто не прошёл.** `uv` не заявлен в
пререквизитах; README-быстрый старт падает без `ddl_init`; пульт вечно висит
на запаused `etl_pipeline`; после `git pull` образ Airflow не пересобирается
(DAG красный); центральное упражнение урока 1 физически невоспроизводимо.
Всё это лежало ровно в вынесенной в риски HITL-приёмке.
## Индекс находок по issue
### Issue 08 (курс) — CHANGES_REQUESTED
- **CRITICAL** `docs/course/lessons/01_kafka_to_clickhouse.md:192-239`
упражнение с `kafka_msg_ts` невоспроизводимо: «перезаливка среза» заменена на
`make generated-history-analytics && make up`, который делает `down -v` и
накатывает эталонный DDL — добавленная менти колонка уничтожается, финальный
SELECT падает; в тексте остался висящий `TRUNCATE` (след нестыковки).
Нужен путь перезаливки без сброса схемы (смена `kafka_group_name` /
`CLEAN_START=0` + повторный backfill) и проверка на стенде.
- **HIGH** `docs/course/README.md:19-34`, `README.md:43-47` — первая команда
курса требует `uv` на хосте; ни пререквизиты, ни quick start его не называют.
- **HIGH** `docs/course/lessons/05_monitoring.md:254,259-262,339` — на штатном
пути (без live) `Kafka No Messages Produced` горит постоянно; урок обещает
«алерты не шумят»; панели описаны для потока, которого нет.
- **MEDIUM** `00_kafka_intro.md:56-65` — служебные топики `generator_state` и
`generator_startup_history_manifest` не объяснены (урок построен на
разглядывании списка топиков).
- **MEDIUM** `00_kafka_intro.md:117-122` vs `05_monitoring.md:156-161`
взаимоисключающие утверждения о видимости consumer lag; проверить на стенде.
- **MEDIUM** `README.md:64-68`, `docs/OPERATIONS.md:175-199`
`make generated-history-check` после `PROFILE=daily-wave` падает: в check
зашит диапазон ci, PROFILE не передаётся.
- **MEDIUM** `docs/OPERATIONS.md:95` — источником STG первым назван архивный
`make data`.
### Issue 12/14 (пульт, профиль) — CHANGES_REQUESTED
- **HIGH** `airflow/dags/generator_control_dag.py:227-235` +
`etl_pipeline_dag.py:263``etl_pipeline` создаётся на паузе; `trigger_etl`
(`wait_for_completion`, без `execution_timeout`) виснет навсегда,
`max_active_runs=1` блокирует повторные запуски. В Airflow 2.10 нет
`fail_when_dag_is_paused` (Context7). Нужна предпроверка «etl_pipeline не на
паузе» + шаг unpause в README/runbook.
- **HIGH** `docker-compose.yml:293-294,334`, Makefile (`down`, `clean`) —
профильный `live-generator` не удаляется `down -v` (доказано экспериментом,
Compose v5.1.3); `restart: unless-stopped` воскрешает его после перезагрузки.
В `down`/`clean`/CLEAN_START нужен `--profile live-generator`.
- **MEDIUM** `README.md:32-40` — быстрый старт без `ddl_init`: precheck падает
сырым `UNKNOWN_TABLE`; добавить шаг и человекочитаемую подсказку.
- **MEDIUM** `airflow/requirements.txt:9-10`, Makefile `up` — после `git pull`
образ Airflow не пересобирается → `ModuleNotFoundError: prometheus_client`,
DAG красный. Нужен `--build` или инструкция.
- **MEDIUM** `docs/OPERATIONS.md:237-241` vs `kafka_io.py:161,253``sleep 5`
меньше времени старта continue (≥10 с): live-строк ноль, `CHECK_LIVE_SEAM=auto`
молча пропускает шов. Было `sleep 130` при тике 60 с.
- **MEDIUM** `airflow_control.py:102-141` vs `run_generator.sh:56-77`,
`import_startup_history_artifact.sh` — предпроверки только в DAG; консольные
пути тихо смешивают миры. Вынести `assert_stand_clean` в общую точку входа.
- **LOW** `service.py:203-215` — простой стенда ×60: ночь = +30 модельных суток
дыры; предупредить в runbook, советовать reset/повторный импорт.
- **LOW** — фиксация Context7-проверки Airflow API только в `.scratch/`, не в
коде/доках (AGENTS.md требует); сами вызовы для 2.10.5 корректны.
### Issue 09 (state v3, фактура) — CHANGES_REQUESTED
- **HIGH** `state.py:236-241``kafka_io.py:181``service.py:100-102`
поднятие STATE_VERSION молча превращает живой стенд во «второй мир»: v2 —
читаемый, несовместимый state, но version-гейт убивает его до семантики,
`from_dict_safe` даёт `None`, сервис тихо стартует новый мир с `model_t0`
поверх истории. Сверка `manifest.state_version != state.version`
(`service.py:246`) недостижима. Спека (строки 198-204, 245-247) требует
громкий отказ; `IncompatibleStateError` уже существует — использовать его при
известной старой версии и `GEN_STATE_RESET=false`; миграционной заметки в
доках нет.
- **MEDIUM** `check_generated_analytics.sh:13,273-289,342,387-393` — проверка
стыка сверяет шов по env-дефолту (`2026-01-01T06:00`), не по манифесту: после
`daily-wave` голый `make generated-history-check` «доказывает» однородность
фальшивого шва посреди backfill. LEFT JOIN `dds.click` структурно пуст; ODS-блок
проходит на пустом ODS. Брать `model_t_end` из манифеста, требовать ненулевые
ODS-строки.
- **MEDIUM** `generation.py:196-209` — предпосылка issue «fallback не
срабатывает» неверна: срабатывает у ~8% визитов (зонд, seed=4242, N=20000);
выпуск не изменился только потому, что у всех доноров сида строки внутри
click_id идентичны — свойство нигде не зафиксировано; при регенерации сида
с вариативностью ~8% визитов поедет. Зафиксировать свойство сида или
ограничить `GEN_MAX_SESSION_EVENTS` длиной максимального донора.
- Ядро фикса крепкое (проверено исполнением): инвариант «рождение ==
восстановление» держится, RNG не потребляется при restore, микросекунды
чинятся честно, форма распределений не сместилась (сверка с `e2d0684^`).
### Issue 10 (Superset) — CHANGES_REQUESTED
- **MEDIUM** `tests/test_superset_dashboard_config.py:64-76` — тесты сверяют
подмножество параметров: мутация «top-15 по убыванию» в экспорте (row_limit
15→500, order_desc→false и др.) проходит все 5 тестов. Нужно полное сравнение
`params` config-vs-export. Классы «возврат world_map» и «рассинхрон layout»
закрыты честно (мутации падают).
- **MEDIUM** issue `10-*:51-68`, `create_dashboard.py:158-185` — приёмка «по
кадру» выполнена декларативно, чарт никем не отрисован; порядок столбцов после
pivot не гарантирован — задать `x_axis_sort: "Events, pcs"`,
`x_axis_sort_asc: false`; снять кадр при HITL.
- **MEDIUM** `create_dashboard.py:505-520` — удаление дублей по глобальному
совпадению имени: повторный прогон молча удалит одноимённый чарт менти;
сузить по `datasource_id`, задокументировать деструктивность.
- **LOW** `create_dashboard.py:176``tooltipTimeFormat` бессмыслен для
категориальной оси; легенда дублирует ось при единственной серии.
- **LOW** `docs/SUPERSET_DASHBOARD.md:205-211` — путь ручного импорта экспорта
(legacy v0) ничем не проверяется.
## Маршрутизация (предложение)
- **Багфикс-цепочка Codex** (кандидат-порядок по риску): (1) семейство
«смешение миров» — 09-HIGH + 12-HIGH-clean + разъезд предпроверок одним
issue; (2) 12-HIGH-паузы + README/ddl_init/build — «свежий стенд» одним
issue; (3) 08-CRITICAL урок 1 + uv + алерт урока 5 — «курс на чистом клоне»;
(4) ложнозелёные проверки (09-MEDIUM-шов, 10-MEDIUM-тесты); (5) остальное.
- **HITL остаётся HITL** (кадры Superset, живой проход Airflow UI, проход по
урокам) — но пункты 08-CRITICAL и 12-HIGH показывают, что часть HITL дешевле
заменить проверками на чистом клоне в CI-манере.
- Отдельно: третий случай подтверждения находки о родословной — кандидат на
датированную поправку в `references/rationale.md` скилла coordinator-loop.
## Что подтверждено как корректное
Стыковка env DAG→генератор; ветвление DAG; отсутствие потребности в Docker
изнутри Airflow; make-цели/имена DAG/DDL-колонки/числа профилей в уроках; ядро
state v3; замена карты в обоих источниках; 172 unit-теста зелёные.