docs(world): описан жизненный цикл прикладного мира

- Зачем:
  - границы владения Compose и Airflow должны быть видны менти из одного источника истины.
- Что:
  - добавлены спека и ADR жизненного цикла мира;
  - обновлены README, архитектурные справочники и прежние решения;
  - правило полной очистки записано как явное и проверяемое требование к агенту.
- Проверка:
  - документация вычитана через `ai-text-lint` и `writing-for-agents`;
  - выполнена литературная вычитка русского текста;
  - `git diff --check`.
This commit is contained in:
ddadmin committed 2026-08-21 23:02:15 +03:00
1 parent fc5244cc67
commit 7e6554507a
12 files changed
+499 -104

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
# ADR 0013. Жизненный цикл мира ведёт Airflow
Дата: 21 августа 2026 года. Статус: принято. Реализация — тикет #119.
## Решение
Compose поднимает службы стенда. Airflow владеет прикладным миром: топиками
Kafka, DDL прикладных баз ClickHouse, стартовыми данными и позицией модельного
времени. `make` поднимает инфраструктуру и через REST API запускает нужный даг,
дожидаясь его результата.
Безопасный даг `world_initialize` создаёт мир, если тот ещё не создан. На
готовом мире он ничего не меняет и успешно сообщает об этом в журнале.
Разрушительный даг `world_recreate` очищает прикладные базы, топики и позиции
их потребителей, затем запускает `world_initialize` и ждёт его завершения.
Канонический DDL применяет отдельная учётная запись Airflow с правами только на
прикладную структуру стенда.
`world_position` остаётся единственным состоянием пульта:
- переменной нет — стенд ещё не инициализировали;
- `-1` — прежний мир признан недействительным, очистка не завершена;
- `0` — мир очищен и готов к начальной генерации;
- `8` и больше — первый несыгранный день готового мира.
`world_recreate` ставит `-1` до первого удаления и `0` только после успешной
очистки. `world_initialize` принимает отсутствие позиции или `0`, а значение
`8` и больше считает готовым миром. Позиция становится `8` последней задачей,
после генерации стартовых дней и успешного приёма обоих источников. Обычные
даги движения мира работают только с готовой позицией.
## Почему
Прежняя цепочка делила один процесс между Compose, shell-скриптами и Airflow:
одноразовые службы создавали стартовые данные, отдельный сценарий ждал их
приёма, а пересборка сама исправляла внутреннюю переменную пульта. Из-за этой
границы успешная отправка в Kafka выдавалась за создание мира, повторный
`make up` смешивался с инициализацией данных, а узкая пересборка сохраняла в
Kafka сообщения и офсеты прежнего мира.
Airflow уже ведёт последующие дни и приём заказов. Отдав ему начало той же оси,
получаем один наблюдаемый процесс и одну точку успеха: зелёный
`world_initialize` означает, что схема создана, стартовый мир принят и его
следующий день известен.
## Что это решение уточняет
- В [ADR 0007](0007-clickhouse-access.md) DDL больше не принадлежит `etl`:
прикладную структуру ведёт отдельный `lifecycle`.
- В [ADR 0009](0009-world-control.md) отсутствие `world_position` больше не
означает готовый стартовый мир. Начальную позицию ставит
`world_initialize`, а работники принимают только `8` и больше.
- Одноразовые службы Compose и shell-ожидание, описанные прежним устройством
хранилища, отменены. Их последовательность стала наблюдаемым графом Airflow.
## Отклонено
- Оставить создание мира в Compose, а позицию исправлять из
`make rebuild-storage`: работает технически, но сохраняет двух
оркестраторов одного процесса.
- Один безусловно разрушительный даг для первого и повторного запуска: обычный
`make up` мог бы стереть уже прожитые дни.
- Отдельная переменная состояния мира: она дублировала бы
`world_position` и могла разойтись с ней. Значения `-1` и `0` выражают
нужные переходы в одном месте.
- Выдать обычной учётной записи `etl` право удаления баз: рабочий конвейер не
должен получать административные права из-за редкой операции восстановления.