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

14 KiB
Raw Blame History

Кросс-линейное ревью цепочки генератора 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-199make 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:263etl_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,253sleep 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-241kafka_io.py:181service.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:176tooltipTimeFormat бессмыслен для категориальной оси; легенда дублирует ось при единственной серии.
  • 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-теста зелёные.