Commit Graph
168 Commits
Author SHA1 Message Date
ddadmin f7123f2ad7 fix(monitoring): ряды панелей подписаны своими именами, а не буквой запроса
- Зачем:
  - на живом дашборде видно то, чего не показывает API: при нескольких
    запросах в панели Grafana ставит перед именем ряда его букву — «A события»,
    «B приземлилось в ClickHouse». Читателю панели это шум.
- Что:
  - свежесть и поток собраны одним запросом на панель: два столбца вместо двух
    запросов. У потока это заодно вернуло ряд заказов, который пропадал, когда
    в окне не было ни одного слепка.
  - у сходимости ряды из разных источников слить нельзя, поэтому ряду
    ClickHouse имя задано переопределением поля.
- Проверка:
  - make config-test; на живом стенде подписи прочитаны прямо со страницы
    дашборда: «события», «заказы», «произведено в Kafka», «приземлилось в
    ClickHouse»; все шесть панелей отвечают через /api/ds/query.
2026-08-21 13:28:22 -04: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
ddmitry 2ccb8ff72c Merge pull request 'Пол мониторинга: источник ClickHouse в Grafana и экспортёр Kafka' (#118) from feat/114-monitoring-floor into main
Reviewed-on: #118
2026-08-21 17:34:02 +03: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 0bb07fd91d docs(spec): мониторинг перенесён из хвоста в параллельный трек
- Зачем:
  - дашборды нужны точками наблюдения на приёмке этапов 4-8, а зависимость
    у них формально только от этапа 1: «в хвосте» было выбором, не
    необходимостью.
- Что:
  - раздел 9: дашборды строятся параллельно этапам 4-8, состав не меняется.
  - назван хвост этапа: runbook keeper/DDL, system.text_log, правила
    оповещения, урок «посмотрели - сломали - вернули».
  - записано правило: панели порогов и цветовых вердиктов не несут, красное
    и зелёное живёт в проверках.
- Проверка:
  - тикеты этапа нарезаны по этому раскрою: #114-#117 под #11.
2026-08-21 08:06:10 -04:00
ddadmin 67313bf997 chore(main): объединены локальные и удалённые изменения
- Зачем:
  - локальные уточнения документации нужно опубликовать без потери слитых изменений #96 и #109.
- Что:
  - актуальный `origin/main` объединён с двумя локальными коммитами документации.
  - сохранены уточнения применения настроек ClickHouse и границ проектирования заказов.
- Проверка:
  - `make config-test`.
  - `make lint`.
2026-08-21 11:09:49 +03:00
ddmitry fd988babdf Merge pull request 'Упростить имена таблиц заказов в ODS' (#113) from feat/109-simplify-order-table-names into main
Reviewed-on: #113
2026-08-21 11:02:14 +03:00
ddadmin 2a94ee9bde refactor(ods): упрощены имена таблиц заказов
- Зачем:
  - ODS должен показывать сущность заказа, а не способ её доставки слепком.
- Что:
  - физические таблицы и представление переведены на имена `order_*`.
  - SQL загрузки, DAG и документация приведены к тому же словарю.
- Проверка:
  - `make config-test`, `make lint`.
  - `make clean && make up`, `make smoke`, `make check-clickhouse`, `make check-services`.
2026-08-21 10:49:02 +03:00
ddmitry a24dfc4510 Merge pull request 'feat(stand): добавлены стартовые слепки заказов' (#112) from feat/96-start-world-snapshots into main
Reviewed-on: #112
2026-08-21 10:47:45 +03: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 9bb044e1ce docs(orders): убран преждевременный вопрос о _load_id
- Зачем:
  - состав полей DDS определяется вместе с моделью, поэтому отдельная задача заранее не нужна.
- Что:
  - удалена задача #85 из списка открытых решений по заказам.
  - убран устаревший указатель на неё из описания приёма.
- Проверка:
  - выполнен `git diff --cached --check`.
2026-08-20 15:57:37 +03:00
ddadmin 0dcc874419 docs(clickhouse): уточнено применение настроек нод
- Зачем:
  - перезапуск мог оставить прежнее окружение и старый inode файла, а документы обещали неподдерживаемое живое применение.
- Что:
  - в README записано пересоздание нод с ожиданием их здоровья.
  - ADR 0007 отделяет решение от процедуры и очищен от устаревших обещаний.
- Проверка:
  - make config-test.
  - git diff --cached --check.
2026-08-20 15:10:13 +03:00
ddmitry 664abf0160 Merge pull request 'Исправить отрицательный total после складской дельты' (#108) from fix/102-negative-order-total into main
Reviewed-on: #108
2026-08-20 13:17:36 +03:00
ddadmin 64bcd3d596 fix(generator): исправлен расчёт скидки после складской дельты
- Зачем:
  - слепок D7 содержал отрицательный итог заказа и попадал в брак ODS.
- Что:
  - скидка пересчитана от корзины после удаления отсутствующей позиции.
  - сериализатор отклоняет отрицательные деньги через ValueError.
  - добавлены проверки восьми стартовых дней и обновлена опись мира.
- Проверка:
  - make lint, make typecheck и make test в generator/.
  - живой world_next_day: 1694 строки приняты, таблица ошибок пуста.
2026-08-20 13:13:14 +03:00
ddmitry 7b89ccb529 Merge pull request 'feat(clickhouse): справочники вынесены в зону dic, словарь читает подложку' (#107) from feat/105-dictionary-home into main
Reviewed-on: #107
2026-08-20 12:26:39 +03:00
ddadminandClaude Opus 5 5cc0ede75a fix(clickhouse): починена проверка словаря, выметены хвосты переноса
Зачем: ревью в три линии нашло настоящий дефект. Девятая проверка кластера
звала dictGet('dds.products', …) — объекта после переноса не существует, и
make check-clickhouse падал. Промах вырос из недосчёта: упоминания dds.products
я искал с фильтром по расширениям, .sh туда не попал, и в ADR уехало «четыре
упоминания» вместо семи.

Что: проверка переведена на dic.products и сверяет цену, умножив её обратно на
сто, — так утверждается ещё и точность приведения к Decimal. Раздел 3
мастер-спеки и список пользователей README догнали перенос: оба описывали
отменённое устройство. В ADR 0012 добавлено условие пересмотра для дома — для
него его не было, хотя ради дома тикет и заводился; недосчёт записан в
«Что проверено» как урок. Разнобой обозначений сведён: карта таблиц в доке
хранилища перешла на имена баз строчными, таблица мастер-спеки — на заголовок
«Где», зону всюду зовут зоной, а не слоем. Термин «Справочник» переписан без
метафоры и уложен в формат глоссария. Вычтено лишнее: комментарии DDL
сократились вдвое, из ADR 0006 и 0007 убраны самооправдание и дублирующие
абзацы, отраслевой образец получил честную оговорку о непроверяемости.

Проверка: make config-test, make lint, make smoke и make check-clickhouse —
все зелёные, десять проверок кластера из десяти.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:10:03 +03:00
ddadminandClaude Opus 5 d77acfdf6c fix(docs): расхождение нод при обновлении словаря названо верно
Зачем: горячее ревью поймало ошибку в трёх местах сразу. Было записано, что
после правки каталога ноды расходятся до тридцати секунд — число взято из
ширины окна LIFETIME(MIN 60 MAX 90). Ширина тут ни при чём: каждая нода ждёт
правки от нуля до верхней границы окна, фазы у них независимы, поэтому
расходиться они могут почти на все полторы минуты.

Что: исправлены ADR 0012, опорная точка мастер-спеки и комментарий DDL. Заодно
уточнено, что LIFETIME(0) неатомарность не предотвращал, а сжимал до разброса
исполнения SYSTEM RELOAD DICTIONARY ON CLUSTER, и что витрина выручки пока
спроектирована, а не построена.

Проверка: рассуждением, замером не подтверждалось — прежнее число тоже было
выведено, а не измерено. Замер в ADR (116 секунд на окне MIN 60 MAX 120)
относится к другому утверждению: что словарь обновляется сам.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:47:23 +03:00
ddadminandClaude Opus 5 b3e66034fc docs(storage): устройство справочников оставлено в одном месте
Зачем: каждое утверждение о словаре было записано трижды — в доке хранилища, в
комментариях DDL и в ADR 0012. Правка поведения стоила бы трёх согласованных
правок, а расхождение между копиями обнаружилось бы не сразу. Дока хранилища —
карта, а не пересказ реализации.

Что: раздел «Справочники» сведён к своему уровню — правило принадлежности зоны,
топология на кластере, единицы денег и указатели. Механика (движок, форма пути,
пользователь, окно обновления) осталась только в sql/ddl/05-dic-catalog.sql,
рядом с кодом, который она объясняет. Блок «Что проверено» отправлен к замерам
ADR 0012 вместо их пересказа — так же, как он уже поступает с ADR 0005.

Проверка: минус 30 строк; ссылок на убранный подзаголовок в репозитории нет,
три относительные ссылки нового текста разрешаются в существующие файлы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:42:26 +03:00
ddadminandClaude Opus 5 abc94ba406 feat(clickhouse): справочники вынесены в зону dic, словарь читает подложку
Зачем: место словаря в dds было унаследовано от первого объекта, а не решено.
Справочник читают несколько слоёв, а производит его в хранилище ни один —
прописка внутри одного из потребителей приписывала DDS владение, которого у
него нет.

Что: заведена база dic вне цепочки STG → ODS → DDS → DM, dds.products переехал
в dic.products. Под словарём появилась подложка dic.products_file на движке
File — намеренное усложнение ради урока: в бою источник словаря приезжает
процессом, а не лежит файлом у сервера. Источник объявлен формой query и
приводит цену из целых копеек каталога в Decimal(18, 2), как у денег бэкенда.
Обновление — окном LIFETIME(MIN 60 MAX 90) вместо ручной перезагрузки, ценой
заявленной неатомарности между нодами. У словаря свой беспарольный
пользователь dict с единственным правом на чтение dic. Суффикс _file добавлен
в конвенцию имён ADR 0006. Решение, отвергнутые варианты и условия пересмотра
— ADR 0012.

Проверка: make clean && make up на собранном заново стенде — зелено, make smoke
20 проверок и 0 ошибок, make lint чисто. Словарь LOADED со 180 строками, цена
Decimal(18, 2) и точна на копейках: 188990 → 1889.90. Пользователь bi читает
словарь dictGet-ом со второй ноды, analyst — подложку соединением, база dds
пуста. Минимальное право на движок замерено тремя пользователями: достаточно
GRANT FILE ON *.*, хотя отказ называет TABLE ENGINE ON File.

Closes #105

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:37:26 +03:00
ddmitry 862dde9fa4 Merge pull request 'feat(clickhouse): добавлен словарь товаров из каталога' (#104) from feat/95-product-dictionary into main
Reviewed-on: #104
2026-08-19 07:38:16 +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
ddmitry 62b275ef94 Merge pull request 'feat(orders): добавлен версионный ODS заказов' (#103) from feat/94-versioned-order-ods into main
Reviewed-on: #103
2026-08-18 23:58:54 +03:00
ddadmin a5508bd9ec refactor(orders): SQL приёма вынесен из DAG
- Зачем:
  - преобразования хранилища должны читаться рядом с целевым слоем, а DAG должен показывать оркестрацию.
- Что:
  - запросы забора и разбора заказов разложены по каталогам STG и ODS.
  - общий контракт провода подключён в обе ветви штатным шаблонизатором Airflow.
  - SQL смонтирован во все службы Airflow, а правило раскладки записано в архитектуре.
- Проверка:
  - make lint config-test smoke check-services.
  - airflow tasks render для pull_batch и parse_batch; airflow tasks test для parse_batch.
2026-08-18 23:55:04 +03:00
ddadmin bdcbe48b59 feat(orders): добавлен версионный ODS заказов
- Зачем:
  - менти должен различать версию заказа, наблюдение источника и запуск загрузки.
- Что:
  - добавлены таблицы версий и брака заказов, а также представление ods.order_v.
  - даг orders_ingest дополнен строгим переходом одного среза STG в две цели ODS.
  - результаты опытов записаны в документации, дефект генератора вынесен в #102.
- Проверка:
  - make lint config-test smoke check-clickhouse check-services.
2026-08-18 23:10:01 +03:00
ddmitry 11a494f550 Merge pull request 'feat(orders): добавить приём слепков в STG' (#101) from feat/93-orders-ingestion into main
Reviewed-on: #101
2026-08-18 21:49:08 +03:00
ddadmin 6bd9190142 feat(orders): добавить приём слепков в STG
Зачем:
- связать проигрывание модельных дней с пакетным приёмом заказов
- показать на одном стенде различие потокового push и пакетного pull

Что:
- добавлен топик, Kafka-чтец и реплицированное сырьё заказов
- добавлен даг orders_ingest с одним прямым чтением и идентификатором загрузки
- работники мира отправляют слепок, ждут приём и только затем двигают позицию
- решения, границы отказа и проверки отражены в ADR и архитектурных документах

Проверка:
- make lint
- make config-test
- make smoke
- make check-clickhouse
- make check-services
2026-08-18 21:33:13 +03:00
ddmitry e67ee27a17 Merge pull request 'feat(generator): добавлена команда слепков заказов' (#100) from feat/92-order-snapshot into main
Reviewed-on: #100
2026-08-18 17:17:16 +03:00
ddadmin 8e21381e13 feat(generator): добавлена команда слепков заказов
- Зачем:
  - задача #92 добавляет второй источник и показывает слепок как чистую функцию зерна и дня.
- Что:
  - добавлены окно, состояние на границе суток и канонические байты с настоящими миллисекундами.
  - добавлены команда snapshot, сдвиг D → D−1 и переиспользование проигранных дней.
  - опись дополнена хешами слепков и поведенческими тестами.
- Проверка:
  - в generator выполнены make lint, make typecheck и make test: 427 тестов.
2026-08-18 17:12:49 +03:00
ddmitry c11da3b252 Merge pull request 'feat(generator): добавлена судьба заказов' (#99) from feat/91-order-fate into main
Reviewed-on: #99
2026-08-18 16:06:32 +03:00
ddadmin 0d90bd566b feat(generator): добавлена судьба заказов
- Зачем:
  - issue #91 требует решать исход и расхождение заказа в день его рождения.
- Что:
  - добавлены три исхода, моменты оплаты и отмены и дельта одной позиции.
  - обновлены проверки повторяемости и архитектурное описание заказов.
- Проверка:
  - cd generator && make lint && make typecheck && make test (418 passed).
2026-08-18 16:02:12 +03:00
ddmitry 62941d2ee5 Merge pull request 'feat(generator): заказы дня — проекция покупок с деньгами магазина' (#98) from feat/90-order-projection into main
Reviewed-on: #98
2026-08-18 15:19:14 +03:00
ddadminandClaude Opus 5 db632c2558 test(generator): анонимность события проверяется и текстом, не только числом
- Зачем:
  - холодное ревью Кодексом (линия дефектов) показало дыру в проверке
    анонимности: личность, уехавшая строкой в сыром `ecommerce`, проходила
    мимо сравнения числовых колонок, и тест оставался зелёным. Дыра
    воспроизведена: user_id, дописанный в блок покупки, тест не замечал.
- Что:
  - тест анонимности спрашивает двумя способами: числовые колонки дня — со
    всеми личностями аудитории, строки покупок целиком — с личностями
    покупателей. Сырой блок собирается руками, и лишнее поле в нём теперь
    красит проверку.
- Проверка:
  - из `generator/`: make lint, make typecheck, make test (417 тестов).
  - мутация «дописать user_id в ecommerce покупки»: до правки тест зелёный,
    после — красный.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:46:16 +03:00
ddadminandClaude Opus 5 b2fcc7e593 feat(generator): заказы дня — проекция покупок с деньгами магазина
- Зачем:
  - тикет #90: второй источник должен смотреть на торговую половину дня
    глазами бэкенда — заказ есть проекция покупки, а не второе порождение;
    учебный результат — два источника согласованы по построению, а не
    сверкой.
- Что:
  - `commerce` отдаёт вторым выходом покупки дня: номер заказа, корзину,
    выручку клиента, промокод и человека за кукой; новых бросков в
    подпоток `COMMERCE` не добавилось, события дня не сдвинулись.
  - заведена заказная сторона: подпоток `ORDERS` на позиции 2 дерева зерна
    (прежнее мёртвое имя `DISCREPANCIES`), ветвление — по дню рождения
    заказа; `LATECOMERS` не тронут, его наполнит этап 6.
  - новый модуль `orders.py`: деньги заказа целыми копейками —
    `items_total` из корзины, `discount` по таблице «код → скидка»
    (округление вниз), `delivery` броском по таблице весов на полную длину
    дня, `total` = `items_total` − `discount` + `delivery`.
  - план состава завёл `person_id` — последним броском когорты, после
    паспортов: вторая кука пары повторяет ID своего человека, заказ
    показывает его как `user_id`, в событие Метрики он не попадает.
  - `world`: окно изменяемости `ORDER_WINDOW_DAYS` = 7 рядом с D0 и поясом,
    черновая таблица стоимости доставки.
  - слепок дня в тестах сторожит обе половины: заказы сравниваются наравне
    с потоком событий.
  - доки: имя подпотока заказной стороны и состав денег заказа записаны в
    `docs/architecture/orders`, README генератора знает про новый модуль.
- Проверка:
  - из `generator/`: make lint, make typecheck, make test (417 тестов);
    в корне — make lint.
  - опись мира не покраснела: байты восьми дней те же, заказная сторона
    события не сдвинула.

Closes #90

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:30:13 +03:00
ddmitry 716eafe865 Merge pull request 'Добавить каноническое чтение актуальных событий через ods.event_v' (#97) from feat/86-event-v into main
Reviewed-on: #97
2026-08-18 11:46:30 +03:00
ddadmin ded0dac5e2 feat(ods): добавлено представление актуальных событий
- Зачем:
  - потребителю ODS нужна точная актуальная версия события без знания о FINAL.
- Что:
  - создано ods.event_v над распределённой таблицей событий с FINAL.
  - сверка стартового мира переведена на новое представление.
  - документация различает физические версии ODS и бизнес-представление DDS.
- Проверка:
  - make config-test.
  - make check-clickhouse.
2026-08-18 11:33:59 +03:00
ddadminandClaude Fable 5 5f72e63b14 docs(specs): модель DDS заказа перенесена из этапа 3 в этап 4
Зачем: черновик этапов держал DDS заказа в этапе 3, а принятый набор
docs/architecture/orders/ оставляет модель DDS проектированию этапа 4
(ingestion.md «Не входит», тикет #85 перевешен на #6) — расхождение
всплыло на холодном ревью нарезки этапа (#89).

Что: в разделе 9 этап 3 сужен до STG/ODS и дага приёма, модель DDS
заказа названа работой этапа 4.

Проверка: чтение; нарезка этапа 3 (#90–#96) согласована с этой строкой.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 22:57:20 +03:00
ddadminandClaude Fable 5 cf1c6c4636 docs(orders): спека превращена в принятый живой набор architecture/orders
Зачем: датированная спека — событие истории, а устройство компонента — живой
документ; большое полотно плохо грузится и агентом, и человеком (ADR 0011).

Что: вычитание #84 слито с переустройством формы: набор
docs/architecture/orders/ — индекс README и семь файлов по частям устройства
(нарезка по правилу «семь плюс-минус два»); датированные файлы удалены,
ссылки перенацелены, AGENTS.md дополнен правилом подпапки. Приёмка
владельцем #88 пройдена, черновой статус снят из README.

Проверка: холодная сверка миграции свежим тредом — потерь решений нет;
обход относительных ссылок набора — битых нет.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 22:05:52 +03:00
ddadmin 3b4827d819 chore(git): каталог .scratch добавлен в игнор
Зачем: локальные рабочие файлы агентов (хэндоффы, обменные артефакты ревью)
живут в репозитории, чтобы переживать перезагрузку, но в git им не место.

Что: .scratch/ в .gitignore.

Проверка: git status не показывает .scratch/.
2026-08-17 22:05:52 +03:00
ddadminandClaude Fable 5 905ca5bbaf docs(orders): спека второго источника собрана из решений развилок
Зачем: решения развилок #70–#74, #80, #81 карты #69 разошлись по резолюциям
тикетов — цельная картина второго источника нужна в одном месте.

Что: черновик спеки заказов docs/specs/2026-08-16-orders.md и спека приёма
docs/specs/2026-08-16-order-ingestion.md; согласующие правки соседних
документов; правки горячего ревью и двух слепых холодных линий — дефекты
сборки закрыты, назван порядок строк внутри слепка.

Проверка: перепроверка находок теми же ревьюерами — ALL_CLOSED.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 22:05:23 +03:00
ddadmin ede1df765c docs(orders): зафиксирован версионный приём заказов
- Зачем:
  - отменённая подмена партиции snapshot_date противоречила порционному чтению Kafka и могла обучать потере ранее принятых версий.
- Что:
  - добавлены спецификация приёма заказов и ADR о версионном ODS с ods.order_v.
  - согласованы мастер-спека, дока хранилища, ADR 0008 и исследование формата.
  - зафиксированы граница приёма, координаты загрузки, диагностические повторы и отложенное проектирование DDS.
- Проверка:
  - git diff --cached --check.
  - горячее ревью по правилам репозитория и принятому решению.
  - два прохода холодного ревью.
2026-08-16 21:42:11 +03:00
ddadmin 8d54a3caba docs(orders): уточнён формат слепка и временные поля
- Зачем:
  - аудит источника нельзя смешивать с бизнес-временем и `_load_ts` ClickHouse.
- Что:
  - зафиксированы JSON-контракт слепка, порядок ключей и намеренное различие `Array(Float64)` и `Decimal`.
  - разведены бизнес-время, аудит источника и загрузка; уточнены слой ODS, `snapshot_date` и технический ключ партиции.
  - добавлены термины словаря и исследование точного миллисекундного формата с проверками ClickHouse.
- Проверка:
  - выполнен `git diff --cached --check`.
2026-08-16 17:56:46 +03:00
ddadminandClaude Opus 5 c17c5ef995 docs(domain): опись мира — вернуть счёт событий и паспорт
Зачем: определение описи разошлось с файлом, который называет. Формула
«счётчики закрытых окон с названным читателем» не покрывает счёт событий по
дням, а он в описи лежит и сегодня, и читатель у него самый живой: make
check-clickhouse сверяет по нему приход в ods.event.

Что: счёт событий вернулся в определение строкой дня; «паспорт» снова
раскрыт скобкой — двумя термами выше стоит «Паспорт куки», и голое слово с
ним сталкивалось; вернулось «единственное, что о мире хранится в git» —
определяющее свойство описи, а не следствие. Строка про слепок и правило
«счётчик только с названным читателем» сохранены.

Проверка: определение сверено с data/world-inventory.json и с резолюцией
«Места заказов в стартовом мире» (#74).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 12:30:39 +03:00
ddadmin 86e2d6dbb2 docs(domain): уточнены слепки и опись мира
- Зачем:
  - документация не должна обещать постоянные числа для движущегося мира.
- Что:
  - уточнено появление опоздавших заказов в слепках.
  - опись ограничена неизменяемым выходом и счётчиками с читателем.
  - счётчик пар оставлен плану, но исключён из описи.
- Проверка:
  - CONTEXT.md перечитан целиком; git diff --check.
2026-08-16 12:26:10 +03:00
ddadminandClaude Opus 5 34267e8494 docs(spec): рамка — чем меряется генератор
Зачем: взгляд владельца на генератор жил только в разговорах, и каждый
следующий агент восстанавливал его заново или не восстанавливал вовсе.
Без него решения по генератору принимаются не по тому критерию: внутренняя
стройность вместо правдоподобия данных на выходе.

Что: новый раздел «Чем меряется генератор» сразу после «Зачем» — пять
утверждений рамки. Генератор меряется выходом, а не устройством; менти
внутрь не смотрит, поэтому конструкция оправдана только наблюдаемым
эффектом; повторимы эффекты, а не числа, и хеши описи обещают видимость
сдвига, а не постоянство чисел; цена ошибки мала, поэтому оборона дороже
ошибки не заводится; сложность изолирована службой ради дешевизны правки.

Рамка шире этапа 2, поэтому спеки следующих этапов ссылаются на неё, а не
копируют.

Проверка: правка только текстовая, кода не касается.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 12:09:11 +03:00
ddadmin c656ca198f docs(domain): уточнены роли человека и посетителя
- Зачем:
  - генератору нужен единый язык для скрытой личности и наблюдаемых идентификаторов.
- Что:
  - определены человек, посетитель и пользователь магазина.
  - закреплена граница между ClientID кликстрима и user_id заказа.
- Проверка:
  - документ перечитан целиком.
2026-08-16 11:56:57 +03:00
ddmitry f51048dd23 Merge pull request 'feat(airflow): пульт мира — работники, выключатель, контейнер' (#83) from feat/79-world-control into main
Reviewed-on: #83
2026-08-13 15:09:19 +03:00
ddadminandClaude Opus 5 292302e161 fix(airflow): правки пульта мира по двум линиям ревью
Зачем: линия дефектов нашла опору на дефект провайдера, линия постановки —
незаписанный ответ на вопрос ADR 0009 и переменную образца, чьё значение на
чужой машине неверно, а узнаёт об этом читатель через двести строк.

Что: `auto_remove` у прогона генератора переведён с `success` на `force`.
Значение `success` тоже убирало контейнер в любом исходе, но случайно —
удаление у провайдера 4.5.7 стоит в `finally` вопреки собственной
документации; `force` то же поведение называет прямо и переживёт починку.
У выключателя назван `catchup=False`: умолчание Airflow 3 то же самое, но
у дага с тиком в 25 минут и `start_date` в январе это первый вопрос
читателя. «Быстрый старт» предупреждает про `DOCKER_GID` — единственное
значение образца, неверное вне этой машины. В образе Airflow записано, что
провайдер docker приходит с базой (4.5.7 к 3.3.0), — это ответ на вопрос,
который ADR 0009 оставил тикету. Список томов планировщика получил ту же
пометку «правя одно, правьте второе», что стоит у числа дней. Проход на
вычитание срезал три комментария, пересказывавших ADR.

Проверка: `make lint`, `make config-test`, `make smoke` (20/0),
`make check-clickhouse` (9/9) — зелёные. Живой пульт на чистом стенде:
снятый с паузы `world_live` сыграл два дня подряд без нажатия (24 м 07 с и
24 м 06 с, пауза между ними 55 секунд), дочерний прогон виден ссылкой из
задачи выключателя; пауза остановила мир на границе суток — тик прошёл,
третьего прогона нет. Контейнер после прогона с `force` не остался.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:57:59 +03:00
ddadminandClaude Opus 5 35dc933d29 feat(airflow): пульт мира — работники, выключатель, контейнер
Зачем: модельный день прогонялся только руками, и владельцу нечем было
проверять процессы стенда вживую. Пульт нужен раньше этапа 3 и независимо
от него: он обкатывает то, что приёму заказов понадобится готовым — вызов
генератора из задачи Airflow.

Что: `dags/world_control.py` — три дага по ADR 0009. Работники
`world_next_day` (день пачкой, «сколько дней» параметром) и `world_live_day`
(день в темпе) живут без расписания и без паузы; выключатель `world_live`
создаётся на паузе, тикает раз в 25 минут и дёргает работника живого дня
с ожиданием конца. Генератор зовётся `DockerOperator` в каноническом
контейнере: сокет докера отдан планировщику, потому что при LocalExecutor
задачи исполняет он, а GID группы `docker` уехал в `.env` как локальная
настройка. Позицию на оси ведёт переменная `world_position` — её ставит
сыгравший день работник и только по успеху. Факты стенда — образ, сеть,
брокер, топик, размер стартового мира — даги получают окружением от compose;
внутри compose они названы по разу якорями, иначе разошлись бы с разовой
службой генератора. README получил раздел про пульт с названной вслух платой
за сокет.

Проверка: `make lint`, `make config-test`, `make smoke` (20/0), `make
check-services` (7/0), `make check-clickhouse` (9/9) — зелёные. На чистом
стенде: два прогона `world_next_day` подряд двигают позицию на два дня,
«дней = 3» — на три, все пять дней доехали в ODS; обрыв контейнера позицию
не двигает, повторный запуск играет тот же день с тем же счётом событий.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:09:14 +03:00
ddadminandClaude Opus 5 61bc156d6a docs(adr): решён пульт мира — работники, выключатель, контейнер
Зачем: модельный день прогоняется только руками, и владельцу нечем
проверять процессы стенда вживую. Этап 3 без дага не существует вовсе
(ADR 0008), поэтому форму пульта и способ вызова генератора надо решить
раньше кода.

Что: ADR 0009 — работники без расписания (`world_next_day` пачкой,
`world_live_day` в темпе) и выключатель `world_live` без своей работы,
ждущий дочерний прогон триггером, а не сенсором; генератор зовётся
`DockerOperator` в каноническом контейнере, потому что в образе Airflow ему
не жить (Python 3.14 против 3.13); позиция ставится как «последний сыгранный
+ 1» — правило вместо сторожа, и его мягкий худший исход назван. Плата за
сокет докера снята замером: доступ открывается добавлением GID в контейнер,
правка прав на хосте не нужна, а сам GID — локальная настройка в `.env`.
Пакетная автоматика отложена, не отвергнута. Расхождения внесены в спеку
генератора: позицию ставит сыгравший, у живого дня появился выключатель.
В словарь добавлен «пульт мира».

Проверка: `make config-test` зелёный; API Airflow 3.3 (пауза и ручной
запуск, ожидание триггера по идентификатору прогона, привязка сенсора к
логической дате, `DockerOperator`) сверено через MCP Context7; версия
Python в образе и права сокета — замером.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 12:35:30 +03:00