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>
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
# Кросс-линейное ревью цепочки генератора 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-теста зелёные.
|
||||||
Reference in New Issue
Block a user