Commit Graph
30 Commits
Author SHA1 Message Date
ddadmin 466acafce6 docs(handbook): интерактивная карта слоев хранилища
Зачем: путеводитель рассказывает путь данных через слои словами, а карта
дает его увидеть и потрогать: у одного смысла в каждом слое свое имя и своя
поверхность, а переносят данные три разных механизма — матвью непрерывно,
даг пачкой, представление без копии. Постановка — тикет #222, форма —
резолюция #208 (один самодостаточный HTML, исходники рядом).

Что: docs/handbook/architecture-map.html — готовая карта; исходники в
docs/handbook/architecture-map/ (map.yaml — слои, узлы, стрелки, карточки;
template.html — разметка, стили, скрипт; render.py — сборка и --check;
README.md). Щелчок по узлу открывает карточку и подсвечивает путь, щелчок по
стрелке — кто и каким SQL переносит данные; переключатель потока приглушает
чужую часть карты. Ссылки карточек ведут в справочники и DDL на Gitea.
Путеводитель и глава о хранилище ведут на карту; make lint проверяет ruff
для сборщика и свежесть рендера; testing.md и AGENTS.md обновлены.

Проверка: uv run docs/handbook/architecture-map/render.py --check и
make lint зеленые; внешних запросов в HTML нет; ссылки и якоря карточек
сверены с Gitea; карта прощелкана в headless Chromium на ширинах
1280–1920: подсветка пути, фильтр, Esc, карточки узлов и стрелок.
Ревью: постановка — Opus; реализация — Codex (Sol), два слепых ревью Opus
(уместность и код), вычитка прозы Sol.
2026-09-03 03:40:14 -04:00
ddadminandClaude Fable 5.1 59d8c83b17 docs(glossary): четыре термина переименованы, world.md и stand.md вычитаны
- Зачем:
  - справочники читает человек со стороны, а «пульт мира», «опись мира»,
    «работник» и «сырьё» звучали шуткой или калькой времён разработки
    (тикет #214, выбор владельца).
- Что:
  - CONTEXT.md: статьи «Паспорт мира», «Даги модельного времени»,
    «Шаговый даг», «Сырые данные», старые слова в строках «Избегать»;
    пометки в ADR 0005, 0008, 0009 без переписывания решений.
  - замены по README, справочникам, путеводителю, лабам, спекам,
    комментариям дагов и SQL, тегу дага и панели Grafana; имена в коде
    не тронуты.
  - world.md и stand.md: 35 находок вычитки внесены, регистр выровнен.
- Проверка:
  - make config-test; make lint в корне и в generator/; make test в
    generator/ (437 passed); скрипт ссылок из #212 пуст; grep старых
    слов вне ADR, research и CONTEXT.md пуст.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QBCtU38qSHg5DvgtnKXcW
2026-09-02 16:46:50 +03:00
ddadminandClaude Fable 5.1 bad5981904 docs(architecture): операторские разделы README переехали в справочники
Зачем: README служил складом для двух зон без своих справочников —
«службы и доступ» и «мир и его пульт». Раскладка принята резолюцией
#211: README остается лицом, устройство живет в docs/architecture/.

Что: новые docs/architecture/stand.md («Стенд: службы, доступ,
настройка», доводы — ADR 0001 и 0007) и docs/architecture/world.md
(«Мир и его пульт», доводы — ADR 0009 и 0013). Доводы смоука и
описания целей make слиты в testing.md без повторов: фраза о
check_containers_survived и абзац про config-test умерли как дубли.
На месте каждого переехавшего раздела README — абзац со ссылкой,
адреса и пароли интерфейсов остались. Путеводитель и README
генератора ссылаются на world.md вместо якорей README. Переехавший
текст не правился, только относительные ссылки.

Проверка: скрипт ссылок из тикета — пустой вывод; make config-test —
ЗЕЛЁНО; подиффовая сверка переехавших диапазонов с README@2588515 —
без расхождений (слепое ревью); в новой прозе нет «ё».

Closes #212

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QBCtU38qSHg5DvgtnKXcW
2026-09-01 22:49:23 +03:00
ddadminandClaude Fable 5 9b1826f5ff feat(generator): откалиброваны доли заказной стороны
- Зачем:
  - семь мест обещали калибровку черновых долей «при пересборке
    эталонного мира (этап 7)», а пересборки не будет (#192); числа
    сверены замером и зафиксированы (#194, хвост этапа 7 — #9).
- Что:
  - замер на каноническом мире (28 дней, без транспорта): все доли в
    пределах ориентиров; пометки «черновое» сняты, наблюдаемые доли
    вписаны в комментарии world.py, fate.md и запись «Что проверено»;
  - потеря purchase: наблюдаемых 1,78% при броске 3% — вычеты штатные
    (пары склейки, приоритет отмены), принято решением владельца;
    ориентир класса B в мастер-спеке различает бросок и наблюдаемую долю;
  - веса мира и опись не тронуты: правки world.py — только комментарии.
- Проверка:
  - cd generator && make lint && make typecheck && make test (437 passed);
  - git diff по data/world-inventory.json пуст; grep «черновое|калибровка»
    по generator/src и docs/architecture/orders пуст.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:42:32 -04:00
ddadminandClaude Fable 5 c1b61d81b9 feat(generator): эталонный мир зафиксирован равным стартовому
- Зачем:
  - у дней 8-13 описи не нашлось читателя: обещание эталона в 14 дней
    снято решением владельца 2026-08-30 (#192, этап 7 — #9).
- Что:
  - резолюция перенесена в обе спеки, докстринг описи и словарь CONTEXT.md;
  - добавлен тест: проигранный через CLI день совпадает с хешем описи;
  - сняты замеры: пересборка описи 20,5 с, снимок 460,7 МБ, make test
    176,5 с — записаны в карту проверок и таблицу бюджетов.
- Проверка:
  - cd generator && make lint && make typecheck && make test (437 passed);
  - git diff по data/world-inventory.json пуст.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:53:52 -04:00
ddadmin cc836fc3c5 fix(superset): стабилизирована проверка рабочего контура
- Зачем:
  - холодный запуск второго приложения Superset давал ложный красный результат исправного стенда.
- Что:
  - тяжёлые `create_app()` и `superset test-db` заменены проверкой сохранённого подключения через REST API.
  - добавлены проверка схемы `dm`, безопасная диагностика и краткая запись приёмки с замерами.
- Проверка:
  - `make config-test`.
  - `make smoke`: 21/21 за 25 секунд.
  - три `make check-services` после перезапуска Superset: 7/7 за 71, 67 и 70 секунд; финальный прогон — 7/7 за 70 секунд.
  - контрольный неверный UUID: 5/1, красной стала проверка метаданных.
2026-08-28 12:38:54 +03:00
ddadmin 61e662c19c refactor(etl): упрощена проверка готовности Kafka
- Зачем:
  - верхние смещения можно получить тем же административным клиентом без одноразового Consumer и фиктивной группы.
- Что:
  - чтение хвоста переведено на AdminClient.list_offsets с OffsetSpec.latest.
  - назначение system.one названо рядом с запросом, а документация и запись проверок сокращены и переставлены по смыслу.
- Проверка:
  - make config-test; make lint.
  - живые проверки готового и отсутствующего разделов Kafka; полный etl_pipeline.
2026-08-27 11:17:43 +03:00
ddadmin 12a5c109b2 feat(etl): добавлен дневной конвейер трансформаций
Зачем:
- Один запуск должен одинаково работать из жизненного цикла, вручную после разгона и повторно после отказа.

Что:
- Добавлена последовательная сцепка DDS и DM с ожиданием каждого слоя.
- DDS дожидается смещений Kafka и очереди доставки ClickHouse за общий срок.
- ETL встроен в создание мира и живой день; документация приведена к новому графу.

Проверка:
- make config-test; make lint.
- make smoke: 21/21; make check-clickhouse: 10/10; make check-services: 7/7.
- make rebuild-storage, живой и пакетный дни, искусственный отказ и ручное восстановление ETL.
2026-08-27 02:43:29 +03:00
ddadmin 20b0666d28 fix(airflow): добавлен повтор запуска DAG после индексации
- Зачем:
  - при холодном старте GET уже видел DAG, пока POST запуска ещё считал его устаревшим.
- Что:
  - точный стартовый ответ 404 повторяется до двадцати раз с паузой три секунды.
  - прочие ответы и сетевые ошибки завершают команду без риска двойного запуска.
  - граница повторов объяснена в коде и документации проверок.
- Проверка:
  - выполнены `bash -n scripts/run-dag.sh`, `make config-test` и изолированные сценарии ответов API.
  - выполнены холодный `make up`, `make lint` и `make smoke` (21/21).
2026-08-22 02:20:01 -04:00
ddadmin 8d82679bc1 feat(airflow): подключён официальный провайдер ClickHouse
- Зачем:
  - нужен единый штатный путь исполнения SQL и программных запросов ClickHouse из Airflow.
- Что:
  - добавлен официальный провайдер и подключения типа `clickhouse`.
  - SQL-файлы переданы `SQLExecuteQueryOperator`, а программные запросы — `ClickHouseHook`.
  - разбор заказов собран в одном файле, DDL разделён по задачам, документация и смоук обновлены.
- Проверка:
  - выполнены `make lint` и `make config-test`.
  - собран образ, выполнены `pip check`, `make rebuild-storage`, `make smoke` (21/21), `make check-clickhouse` (10/10) и `make check-services` (7/7).
2026-08-22 01:40:56 -04:00
ddadmin bca1ffa379 fix(world): устранены расхождения после переноса на main
- Зачем:
  - свежий main добавил мониторинг и проявил устаревшие доводы, мёртвые настройки и слишком короткий тайм-аут DDL.
- Что:
  - документация приведена к текущему жизненному циклу мира и составу стенда;
  - удалены лишняя проверка и неиспользуемый YAML-якорь;
  - ожидание ClickHouse возвращено к 300 секундам, а отказы и параметры DockerOperator пояснены.
- Проверка:
  - `make config-test`;
  - `make lint`;
  - `make smoke` — 21/21.
2026-08-21 23:38:45 +03:00
ddadmin 7e6554507a docs(world): описан жизненный цикл прикладного мира
- Зачем:
  - границы владения Compose и Airflow должны быть видны менти из одного источника истины.
- Что:
  - добавлены спека и ADR жизненного цикла мира;
  - обновлены README, архитектурные справочники и прежние решения;
  - правило полной очистки записано как явное и проверяемое требование к агенту.
- Проверка:
  - документация вычитана через `ai-text-lint` и `writing-for-agents`;
  - выполнена литературная вычитка русского текста;
  - `git diff --check`.
2026-08-21 23:02:15 +03:00
ddadmin e5c573bb25 fix(monitoring): панели уточнены по ревью, приёмка снята на стенде
- Зачем:
  - две линии ревью нашли, что колонки панели брака читаются как
    непересекающиеся кучи, «вовремя ли» из ADR 0002 не считается вовсе, а
    отказ от ряда «против вчерашнего» нигде не записан рядом с решением.
- Что:
  - в панели брака столбец назван «Из них в брак»: брак — часть приехавшего,
    а не соседняя куча.
  - панель слепка считает паузу после прошлого слепка — это и есть «вовремя
    ли» из ADR 0002; пустая пауза у самого раннего слепка держится
    обнуляемым типом.
  - отказ от ряда «против вчерашнего» записан в ADR 0002 и в monitoring.md с
    доводом: сдвиг на модельные сутки прописал бы ускорение мира в JSON.
  - в monitoring.md сказано, почему свежесть читает физическую _dist, а не
    ods.*_v, и почему панель слепка не знает окна; довод про сломанный JSON
    оставлен у кода, а не задвоен в доке.
  - README больше не утверждает, что панелей нет.
  - у разбора дашбордов убран флаг -e: он обещал утверждение, которого нет.
- Проверка:
  - make config-test (краснеет на нарочно сломанном JSON дашборда), make lint,
    make smoke 21/21, make check-clickhouse 10/10; все шесть панелей отвечают
    через /api/ds/query. Опыты приёмки и их следы — monitoring.md,
    «Что проверено».
2026-08-21 11:23:05 -04:00
ddadmin 180ef5b94f feat(monitoring): собран дашборд «Данные» из шести панелей
- Зачем:
  - главный дашборд ADR 0002 отвечает на вопрос «с потоком всё в порядке?»;
    три его вопроса — свежесть, брак и сходимость — у предшественника не
    заданы вовсе, и в этом весь смысл экрана.
- Что:
  - добавлен infra/grafana/provisioning/dashboards/data.json: свежесть, поток,
    брак, сходимость, отставание чтения из Kafka и дневной слепок заказов;
    порогов и цветовых вердиктов на панелях нет.
  - в monitoring.md записано устройство дашборда: почему слои у панелей разные
    и как считается сходимость — с доводом и двумя отвергнутыми способами.
  - make config-test разбирает JSON дашбордов тем же доводом, что и файлы
    дагов: сломанный файл Grafana молча оставляет прежнюю версию.
- Проверка:
  - make config-test; на живом стенде make smoke 21/21 и make check-clickhouse
    10/10; все шесть панелей отвечают через /api/ds/query.
2026-08-21 11:06:43 -04:00
ddadmin c5dd9c2d34 feat(monitoring): подготовлена основа мониторинга Kafka и ClickHouse
- Зачем:
  - подготовлен пол мониторинга для SQL-панелей и метрик отставания Kafka.
- Что:
  - добавлены отдельная роль ClickHouse, источник Grafana и Kafka exporter.
  - образ Grafana собирается с закреплённым плагином вне тома данных.
  - Prometheus, смоук и документация обновлены под четыре цели.
- Проверка:
  - make config-test; make lint.
  - make smoke; make check-clickhouse; make check-services.
  - make -C generator lint typecheck test.
2026-08-21 09:56:56 -04:00
ddadmin ba4743ed52 feat(stand): добавлены стартовые слепки заказов
- Зачем:
  - свежий стенд должен показывать отставание ночной выгрузки заказов от кликстрима.
- Что:
  - после событий генератор отправляет семь слепков дней 0…6 в общий топик orders.
  - make up запускает orders_ingest, ограниченно ждёт его завершения и оставляет день 7 первому ходу мира.
  - README и карта проверок описывают второй источник и измеренную цену подъёма.
- Проверка:
  - make clean && make up; make smoke; make check-clickhouse; make check-services.
  - cd generator && make lint && make typecheck && make test.
2026-08-20 19:12:01 +03:00
ddadmin 13abc58d98 feat(clickhouse): добавлен словарь товаров из каталога
- Зачем:
  - витринам нужен общий с генератором каталог, а менти — пример штатного обновления словаря.
- Что:
  - каталог смонтирован read-only в обе ноды и создан как dds.products ON CLUSTER.
  - роли получили dictGet, а check-clickhouse сверяет ответ под etl с исходным CSV.
  - источник, раскладка и явная политика обновления записаны в документации.
- Проверка:
  - make up с чистыми томами; make lint; make config-test; make smoke; make check-clickhouse.
  - в generator: make lint typecheck test — 427 тестов прошли.
2026-08-19 00:50:14 +03:00
ddadminandClaude Opus 5 c9b11224d2 refactor(ruff): линтер стенда говорит на языке дагов, а не младшего образа
- Зачем:
  - target-version по младшему образу (Superset, 3.10) занижал проверку для дагов: они бегут на 3.13, а ruff предлагал им идиомы старее их рантайма. На учебном стенде это вывернуто наизнанку — менти читает и правит именно даги.
  - защита от Superset была верна не по устройству, а по сегодняшнему содержимому одного файла настройки.
- Что:
  - target-version в корневом ruff.toml поднят до py313, комментарий переписан.
  - две находки UP017 в дагах починены: datetime.timezone.utc заменён на datetime.UTC.
  - из карты проверок убран пункт про разную строгость дверей — с равными версиями он потерял предмет.
- Проверка:
  - make lint; make config-test — зелёные.
  - make -C generator lint — зелёный; исходники генератора не менялись.
  - находок ruff в дагах теперь ровно четыре, как обещал тикет: два переформата и два UP017.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:58:48 +03:00
ddadminandClaude Opus 5 e4e5688775 refactor(make): правки по холодному ревью реализации
- Зачем:
  - половина критерия приёмки стояла не там, где решено: предупреждение о ручном равенстве версий ruff адресовано тому, кто правит лок в generator/, а лежало в корневом Makefile.
  - довод «генератор — отдельная сущность» был выписан трижды почти дословно.
- Что:
  - равенство версий и охват корневой цели названы в карте проверок; три примечания к таблице собраны списком.
  - названа цена занижения target-version: даги бегут на 3.13 и модернизаций не получают.
  - шапка generator/Makefile вырезана, корневая сжата до строки, объяснение в ruff.toml укорочено.
  - формулировки в AGENTS.md и README поправлены.
- Проверка:
  - make lint; make config-test — зелёные.
  - make -C generator lint; typecheck — зелёные; исходники генератора не менялись.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:49:54 +03:00
ddadminandClaude Opus 5 6a708cd5aa refactor(make): корень — про стенд, генератор — за своей дверью
- Зачем:
  - корневые цели смешивали два уровня: пять из шестнадцати начинались с cd generator.
  - цель, названная общерепозиторной, охватывала 31 файл Python из 34: даги и Superset не видел ни линт, ни типы.
- Что:
  - lint, typecheck, test, docs и inventory переехали в новый generator/Makefile.
  - корневой lint заведён по коду стенда — dags и infra/superset — с явными путями и закреплённой версией ruff.
  - заведён корневой ruff.toml: тот же список правил, target-version по младшему Python в образах стенда.
  - цели корня сгруппированы по использованию, осталось двенадцать.
  - два файла дагов переформатированы под новую проверку.
  - карта проверок, оба README, спека генератора и AGENTS.md приведены к двум дверям.
- Проверка:
  - make lint; make config-test — зелёные.
  - make -C generator lint; typecheck; test — зелёные, 407 тестов.
  - ruff check --show-files: из корня ровно три файла стенда, из generator/ — только его.
  - цена корневого lint замерена (0,4 с) и вписана в карту проверок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:31:07 +03:00
ddadmin 6f5f76fafb docs(stage-2): приёмка этапа 2 — снятые замеры и решение по дублям Kafka
Зачем: этап 2 принят прогоном с нуля, и два его следа должны остаться в
доках — иначе решение «не проверяем» станет забытым долгом, а цена цели
разойдётся с замером.

Что:
- в спеке v2 раздел 11 больше не держит Kafka Engine на двух нодах: дубли и
  раскладку партиций между прогонами решено не проверять — дубль возможен по
  устройству движка, окно разобрано в доке хранилища, в ODS его схлопывает
  ReplacingMergeTree. Половина, которую показал #37, названа;
- в карте проверок цена check-services 44 с -> 59 с и абзац с замерами
  приёмки: clean+up 2 м 58 с, смоук 9 с, check-clickhouse 8 с, test 71 с.
  Причина подорожания не выдумывается — названа неизвестной.

Проверка: make clean && make up, затем make smoke, make check-clickhouse,
make check-services, make lint, make typecheck, make test — всё зелёное
7 августа 2026 года.
2026-08-07 20:14:35 +03:00
ddadmin c696fce40b fix(stand): находки ревью — диагноз не утверждает причину, комментарии не врут
Зачем
Холодное ревью нашло три места, где написанное сильнее сделанного.

Что
- Непустая таблица брака больше не выдаётся за доказательство сломанного
  разбора. Модельного дня у брака нет, обрамить его нечем, и строки прежних
  уроков лежат в нём месяц: после первого же урока с мусором проверка
  давала бы неверный диагноз навсегда. Теперь она даёт признак, по которому
  причину отличают, — сошлась недостача с числом брака или нет.
- Комментарий у world-init обещал, что расхождение числа дней с
  STARTING_DAYS поймают счётчики. Это неправда в одну сторону: лишний день
  ложится за рамкой дат описи. Обещание убрано, дыра названа.
- Довод «даг next_day этапа 5 продолжит ось» опирался на несуществующий
  этап; заменён настоящей причиной — заливка замыкает цепь разовых служб.
- Потолок ожидания 300 с получил обоснование замером с кратностью, а сам
  скрипт — честную оговорку: его обещание работает на пустом стенде, на
  живом ждать нечего.
- Третья, пропущенная ссылка на снятый порог скорости дня убрана из спеки.
- Даты замеров в карте целей разведены: #42 менял три цели, а не шесть.

Проверка
Обе ветви диагноза сняты заново на живом стенде: без брака — «не доехали»,
с браком — признак различения. Стенд восстановлен, все 9 проверок зелёные,
брака 0. make lint, typecheck, config-test, test (407 тестов) зелёные.

Ссылка: #42
2026-08-07 18:47:13 +03:00
ddadmin 7c9eeedc40 feat(stand): make up наполняет стенд стартовым миром, опись сторожит его
Зачем
Стенд поднимался пустым, и всякая приёмка следующих этапов начиналась с
ручной заливки данных. Теперь `make up` сам приводит стенд к одному и тому
же состоянию, а в git лежит то, чем это состояние проверяется.

Что
- Опись мира `data/world-inventory.json`: паспорт (зерно, версия
  генератора, хеш каталога) и по строке на каждый из восьми дней — дата,
  число событий, хеш байтов. Собирается `make inventory`, свежесть сторожит
  `test_inventory.py` — тем же способом, что свежесть описания выгрузки.
- Разовая служба `world-init` вышла из-под профиля и играет в топик восемь
  дней при каждом подъёме; зависимый у неё — `airflow-init`, иначе `--wait`
  считает успешно отработавшую службу упавшей.
- `scripts/wait-for-world.sh` — вторая половина `make up`: приём
  асинхронный, поэтому ждать надо доезда до `ods.event`, а не завершения
  заливки. Ограниченный цикл опроса, не пауза наугад.
- Девятая проверка `make check-clickhouse`: подневный счёт событий против
  описи, рамка по датам стартового мира, счёт через `FINAL`. При
  расхождении называет, где искать, — в событиях или в браке.
- Порог «день ≤ 30 с» снят из спеки генератора в обоих местах: замер дал
  1,7 с, порог был выше факта в восемнадцать раз. На его месте — замеры с
  датой. Раздел 9 спеки закрыт: открытых вопросов не осталось.
- Слова: «манифест» стал описью мира, «зерновой мир» — стартовым миром
  (решение владельца). Оба заведены в словарь CONTEXT.md.

Проверка
`make clean && make up` с нуля — 2 м 50 с, доехало ровно 401 185 событий.
`make check-clickhouse` зелёный (8 с), `make smoke` зелёный (9 с),
`make test` — 407 тестов за 71 с, `make lint`, `make typecheck`,
`make config-test` зелёные.

Что проверка умеет краснеть, снято двумя поломками: снос партиции
2026-06-03 дал диагноз «не доехали до ODS», негодная строка в сырье —
«сломан разбор». Строки опыта убраны, день переигран, счёт вернулся.
Тест свежести проверен молчаливой правкой цены в каталоге: покраснел.

Ссылка: #42
2026-08-07 18:37:01 +03:00
ddadminandClaude Opus 5 30a1e9e567 docs(testing): корректность процессов — в даги DQ, не в цели make
- Зачем:
  - находка «это стоило бы проверять регулярно» решалась заново в каждом
    тикете и каждый раз тянулась в make check-clickhouse. У неё есть
    назначенный дом: даги качества данных, которые придут со следующими
    уровнями хранилища.
- Что:
  - добавлен раздел «Корректность процессов живёт в дагах DQ, а не в целях
    make»: цели make отвечают «стенд собран», свойства данных — работа дага.
  - назван фильтр: про полноту дня, свежесть слоя, сходимость витрины с
    источником — это даг, а не цель.
  - названа учебная сторона: даг идёт по расписанию, пишет историю проверок
    и разбирается как обычная задача Airflow — так качество данных устроено
    в бою.
- Проверка:
  - make config-test

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 17:39:49 +03:00
ddadminandClaude Opus 5 6910440400 feat(ods): типизированное событие, строгий приём и таблица ошибок
Зачем: цепочка Kafka → STG → ODS достраивается последним этажом. Сырьё уже
доезжает (#37), настоящие события в топике есть (#41), а типизированного слоя
не было — событие негде было прочитать колонками, а брак негде увидеть.

Что:
- sql/ddl/20-ods-tables.sql — ods.event_rep/_dist на ReplacingMergeTree с
  версией _load_ts, партиция по EventDate, ключ по разделу 1.3 спеки,
  шардирование cityHash64(ClientID); ods.event_errors_rep/_dist с классом
  брака, своими ключами и сроком жизни в месяц.
- sql/ddl/30-ods-views.sql — две матвью над stg.hits_raw_dist. Годность
  считает предикат из трёх частей, вторая матвью берёт его дословное
  отрицание, класс брака пишется первым совпавшим из трёх.
- Метку времени разбирает parseDateTimeBestEffortOrNull, а не JSONExtract:
  ISO-8601 с суффиксом Z JSONExtract не берёт вовсе. Спека генератора
  обещала обратное — обещание поправлено, форма на проводе не менялась.
- Сверка объявлений (contract-тест) снята из документов и из докстрингов
  schema.py: сверх строгого приёма она ловила только смену типа.
- Документация приведена в соответствие: ADR 0005, дока хранилища и обе
  спеки; группа «сказано по памяти» в доке хранилища опустела.

Проверка: make up && make check-clickhouse (8 проверок, 7,5 с); make lint,
make typecheck, make test (406), make docs без диффа. Разовые опыты при
исполнении — в теле PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:06:46 +03:00
ddadminandClaude Opus 5 d7485217d8 refactor(scripts): clickhouse-smoke.sh переименован в check-clickhouse.sh
Зачем: имя файла осталось от прежней цели smoke-cluster, которой больше нет,
и читатель ищет проверку ClickHouse не там, где она лежит.

Что: git mv scripts/clickhouse-smoke.sh scripts/check-clickhouse.sh, тем же
коммитом — вызов в Makefile и строка в карте целей. Абзац-объяснение в
docs/architecture/testing.md снят: он обещал переименование, которое здесь и
случилось.

Проверка: make check-clickhouse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:33:44 +03:00
ddadminandClaude Opus 5 b8816466d9 docs(testing): интеграционная проверка не живёт постоянной целью
- Зачем:
  - проверка, которая кормит стенд данными, оставляет их в мире менти
    навсегда: у ODS срока хранения нет, и события, которых мир не рождал,
    неотличимы в лабах от настоящих. Решение принято при разборе постановок
    #41 и #43, чтобы оно решалось по карте, а не заново в каждом тикете.
- Что:
  - добавлен раздел «Интеграционная проверка постоянной целью не становится»:
    такой прогон делается один раз при исполнении, след — запись в теле PR.
  - названо требование прибираться за собой и дешёвый способ это сделать:
    модельный день за границей оси мира и снос его партиции.
  - названо, что стережёт цепочку постоянно вместо неё — проверки на
    настоящих данных, которые стенд произвёл сам.
- Проверка:
  - make config-test

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 09:22:56 +03:00
ddadminandClaude Opus 5 5492e6ff3d refactor(config-test): срезан разбор Bash — он не ловил ничего
- Зачем:
  - проверка была сломана с рождения цели: bash -n со списком файлов
    разбирает только первый, остальные уходят ему в аргументы. Из пяти
    скриптов проверялся один, и за всё время этого никто не заметил.
  - чинить незачем: скрипты стенда запускают с той же машины, и
    синтаксическая ошибка вылезает при первом же запуске с номером строки.
    Учебной ценности в проверке нет — из неё не узнаёшь ничего, кроме того,
    что у bash есть ключ -n.
  - держалась она не строчкой, а двенадцатью: обход репозитория, временный
    файл со списком, mapfile и две ветки на пустой список.
- Что:
  - из scripts/config-test.sh убраны разбор Bash и весь аппарат сбора
    списка файлов; 53 строки стали 40.
  - разбор файлов DAG остался и получил комментарий с основанием: их на
    машине не запускает никто, обработчик разбирает их внутри контейнера, и
    ошибка всплывает не сообщением, а молча пропавшим DAG.
  - README и карта проверок больше не обещают проверку синтаксиса Bash.
  - в карте записано, почему проверку не стоит заводить заново.
- Проверка:
  - make config-test зелёный.
  - оставшийся разбор DAG краснеет: незакрытая скобка в dags/test_kafka.py
    роняет цель с SyntaxError и ненулевым кодом; файл восстановлен.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 16:59:44 +03:00
ddadminandClaude Opus 5 7e4d5815a3 feat(smoke): смоук спрашивает Kafka снаружи — по объявленному адресу
- Зачем:
  - деление целей оставило дыру: про Airflow, Superset, Prometheus и Grafana
    смоук стучится с машины в отображённый порт, а про Kafka после переезда
    check_kafka_from_host знал только «контейнер здоров».
  - вердикт этот приходит из healthcheck в compose.yaml, а тот спрашивает
    брокер изнутри и по внутреннему слушателю: объявленный наружу адрес может
    вести не туда, и Kafka всё равно останется здоровой.
  - поломка популярная и показательная: клиент подключается, получает
    метаданные и молча виснет на адресе, которого с его стороны нет. Менти
    узнаёт, что у брокера два слушателя и зачем нужен advertised.listeners.
    Генератор будет писать в Kafka именно с машины.
- Что:
  - check_kafka_external_listener в make smoke: запрос списка топиков с машины
    через отображённый порт, ответ приходит только если объявленный адрес ведёт
    туда же. Комментарий у проверки объясняет, от чего она заведена.
  - ожидание ответа ограничено 15 секундами при замеренных 2,6 — впятеро
    больше, чем стоит зелёный прогон.
  - README и карта проверок: новая проверка названа, доводы записаны, цена
    смоука обновлена с 6 до 8 секунд.
- Проверка:
  - make config-test, make smoke (20 проверок, 8 с), make check-clickhouse,
    make check-services — зелёные.
  - краснеет на своей поломке: брокеру объявлен адрес kafka-nowhere:29092 при
    целом внутреннем слушателе — проверка состояния контейнера осталась
    зелёной, смоук покраснел именно на этой строке. После проверки Kafka
    возвращена в исходное состояние, посторонних контейнеров не осталось.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 15:20:27 +03:00
ddadminandClaude Opus 5 bbbe17cfb0 refactor(smoke): цели проверки по назначению — смоук, ClickHouse, службы
- Зачем:
  - смоук перестал быть быстрым: 42 секунды из 48 съедали шесть проверок,
    которые ждут службу — запуск DAG, вход в Superset, Kafka с машины.
  - имена целей врали: смоуком звались и глубокая проверка кластера, и
    интеграционные проверки; префикс достался им от общего происхождения.
  - нигде не было записано, зачем в репозитории каждая цель и куда класть
    новую проверку, — без записи скрипт дорастёт снова.
- Что:
  - ось деления — кого спрашивают, а не сколько стоит: make smoke (стенд
    собран), make check-clickhouse (спрашивают у ClickHouse), новая
    make check-services (службы работают).
  - шесть тяжёлых проверок переехали в scripts/stand-services.sh; общее —
    счёт, обращение к Compose, зависимости машины и check_containers_survived
    — вынесено в scripts/stand-common.sh, копипасты нет.
  - smoke-cluster переименована в check-clickhouse; имя файла скрипта не
    тронуто (в него встраивается проверка договора со схемой), расхождение
    названо в карте.
  - смоук и check-services печатают своё время в строке ИТОГ; порога по
    времени нет — по доводу ADR 0004.
  - docs/architecture/testing.md: карта всех семи целей, правило быстрого
    смоука словами, лесенка по частоте и правило про краснеющую проверку,
    переехавшее из README; указатель из AGENTS.md.
  - README: описания целей сокращены, карта не дублируется; быстрый старт
    показывает работающий стенд, а не только собранный.
  - планка приёмки этапа в спеке названа поимённо: три цели вместо
    «smoke-проверки».
- Проверка:
  - make config-test, make smoke (19 проверок, 6 с), make check-clickhouse
    (8 проверок, 7 с), make check-services (7 проверок, 44 с) — зелёные.
  - 19 + 7 = 25 разных проверок, как и до деления: check_containers_survived
    считается дважды намеренно.
  - краснеют обе разделённые цели: со снятым prometheus смоук дал три ошибки,
    с подменённым UUID подключения Superset покраснел check-services.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:34:02 +03:00