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
This commit is contained in:
ddadminandClaude Fable 5.1 committed 2026-09-02 16:46:50 +03:00
1 parent 86ad8afaf2
commit 59d8c83b17
54 files changed
+794 -776

No files matched your search

+4 -4
View File
@@ -46,8 +46,8 @@ HITS_PARTITIONS = (0, 1)
READINESS_TIMEOUT_SECONDS = 300
READINESS_POLL_SECONDS = 3
# Позиция на оси модельного времени: номер первого несыгранного дня. Её ставит
# пульт мира, и только она отличает прожитый день от живого — зачем это
# Позиция на оси модельного времени: номер первого несыгранного дня. Её ставят
# даги модельного времени, и только она отличает прожитый день от живого — зачем это
# сессиям, разобрано в sql/dds/session_scope.sql.
WORLD_POSITION = "world_position"
@@ -224,8 +224,8 @@ def _wait_for_phase(
def _world_position() -> int:
"""Позиция мира числом, с внятным отказом на непригодном значении.
Ту же переменную читает пульт, но общего модуля у дагов нет намеренно:
связь между пультом и трансформацией держит сама переменная, а не импорт.
Ту же переменную читают даги времени, но общего модуля у дагов нет намеренно:
связь между дагами времени и трансформацией держит сама переменная, а не импорт.
"""
raw_position = Variable.get(WORLD_POSITION, default=None)
if raw_position is None:
+1 -1
View File
@@ -148,7 +148,7 @@ def build_classes_vs_inventory() -> list[str]:
for mismatch_class, count in day["orders"].items()
}
if not expected:
raise AirflowException("classes_vs_inventory: в описи нет закрытых дней")
raise AirflowException("classes_vs_inventory: в паспорте нет закрытых дней")
query = (SQL_ROOT / "dq" / "classes_vs_inventory.sql").read_text(encoding="utf-8")
# GROUP BY закономерно не возвращает группу из нуля строк.
+2 -2
View File
@@ -1,9 +1,9 @@
"""Приём заказов: пакетный забор слепка из Kafka в хранилище.
Путь Kafka → STG → ODS у заказов один и живёт одним дагом: сначала забор
порции в сырьё, затем разбор того же среза в типизированный ODS и в таблицу
порции в `stg`, затем разбор того же среза в типизированный ODS и в таблицу
брака. Расписания у дага нет: его дёргает тот, кто положил слепок в топик, —
работник пульта мира после проигрыша дня, — и ждёт конца.
шаговый даг модельного времени после проигрыша дня, — и ждёт конца.
Контраст с приёмом событий и есть урок. События тянет матвью, навсегда
подписанная на чтеца: приём идёт сам, пока идёт поток. Слепок заказов
+15 -15
View File
@@ -1,25 +1,25 @@
"""Пульт мира: даги, которыми двигают ось модельного времени.
"""Даги модельного времени: ими двигают ось мира.
Их три, и они двух родов. **Работники** играют день и запускаются руками:
Их три, и они двух родов. **Шаговые даги** играют день и запускаются руками:
`world_next_day` — пачкой, без пауз, сколько дней попросили; `world_live_day` —
один день в темпе модельного времени. **Выключатель** `world_live` своей работы
не делает: он тикает по расписанию и дёргает работника живого дня, дожидаясь
не делает: он тикает по расписанию и дёргает шаговый даг живого дня, дожидаясь
конца. Снят с паузы — мир едет день за днём; поставлен на паузу — встал на
границе модельных суток.
Сыграть день — половина работы. Вторая половина: отправить слепок заказов и
дождаться, пока хранилище его заберёт. Поэтому у обоих работников за проигрышем
дождаться, пока хранилище его заберёт. Поэтому у обоих шаговых дагов за проигрышем
идут отправка слепка тем же контейнером генератора и ждущий триггер дага приёма
`orders_ingest`.
Живой день после установки позиции дожидается еще и `etl_pipeline`. Пачка
только разгоняет источники: слои после нее менти догоняет ручным запуском.
Разделение не косметическое. Расписание на самом работнике заставило бы кнопку
паузы значить две вещи разом — «мир не едет сам» и «даг выключен», — а работник
Разделение не косметическое. Расписание на самом шаговом даге заставило бы кнопку
паузы значить две вещи разом — «мир не едет сам» и «даг выключен», — а шаговый даг
при этом выглядел бы в списке выключенным, хотя нажимают его каждый день.
Календарь Airflow к оси мира отношения не имеет: какой день играть, работник
Календарь Airflow к оси мира отношения не имеет: какой день играть, шаговый даг
спрашивает у переменной, а не у логической даты прогона.
Решения и доводы целиком — ADR 0009.
@@ -51,7 +51,7 @@ ORDERS_TOPIC = os.environ["KAFKA_ORDERS_TOPIC"]
STARTING_DAYS = int(os.environ["WORLD_STARTING_DAYS"])
# Позиция на оси: номер первого несыгранного дня. До конца инициализации
# работники не имеют права угадывать её — состояние объяснено в ADR 0013.
# шаговые даги не имеют права угадывать её — состояние объяснено в ADR 0013.
#
# Позиция ставится, а не увеличивается. Наложись один прогон на другой, худшее
# при таком правиле — сыгранный дважды день, а повтор схлопнет
@@ -63,12 +63,12 @@ WORLD_POSITION = "world_position"
# двадцати четырёх минут: тик только спрашивает «не пора ли снова».
LIVE_TICK = datetime.timedelta(minutes=25)
# Какой день играть, работник узнаёт у первой задачи. Спрашивают её все
# Какой день играть, шаговый даг узнаёт у первой задачи. Спрашивают её все
# генераторные шаги: слепок снимается с той же позиции, что и проигрыш.
PLAYED_DAY = "{{ ti.xcom_pull(task_ids='first_unplayed_day') }}"
START_DATE = datetime.datetime(2026, 1, 1, tzinfo=datetime.UTC)
TAGS = ["пульт мира"]
TAGS = ["модельное время"]
@task
@@ -120,7 +120,7 @@ def _generator(task_id: str, command: list[str]) -> DockerOperator:
def _ingest_orders() -> TriggerDagRunOperator:
"""Задача забора: дёрнуть даг приёма и дождаться, чем он кончился.
Ожидание здесь несущее. Без него работник позеленел бы, не узнав, доехал
Ожидание здесь несущее. Без него шаговый даг позеленел бы, не узнав, доехал
ли слепок, и позиция мира ушла бы вперёд хранилища — а зелёный конец графа
не должен переживать отказ выше (ADR 0003).
"""
@@ -128,7 +128,7 @@ def _ingest_orders() -> TriggerDagRunOperator:
task_id="trigger_orders_ingest",
trigger_dag_id="orders_ingest",
wait_for_completion=True,
# Умолчание — минута опроса, а весь прогон работника пачкой короче.
# Умолчание — минута опроса, а весь прогон шагового дага пачкой короче.
poke_interval=10,
)
@@ -151,7 +151,7 @@ def _ingest_orders() -> TriggerDagRunOperator:
# (params.rst, Airflow 3.3), чтобы правило было видно сразу.
description_md=(
"Форма задает только число дней. Номер первого несыгранного "
"дня работник берет из переменной `world_position` (Admin → "
"дня шаговый даг берёт из переменной `world_position` (Admin → "
"Variables). Поля Logical date и Run ID нужны самому Airflow "
"и на выбор дня не влияют."
),
@@ -163,7 +163,7 @@ def world_next_day():
Запускается руками. День по умолчанию один, но разгон вперёд идёт одним
нажимом, а не десятью: сколько дней играть — параметр запуска. Первый
день работник берет из `world_position`. Логическая дата прогона на выбор
день шаговый даг берёт из `world_position`. Логическая дата прогона на выбор
дня не влияет.
"""
@@ -215,7 +215,7 @@ def world_live_day():
Ускорение ×60: модельные сутки укладываются примерно в двадцать четыре
реальные минуты, и суточная волна разворачивается на глазах. Запускается
руками; чтобы мир жил так день за днём сам, есть выключатель `world_live`.
Следующий день работник берет из `world_position`. Логическая дата прогона
Следующий день шаговый даг берёт из `world_position`. Логическая дата прогона
на выбор дня не влияет.
"""
+1 -1
View File
@@ -185,7 +185,7 @@ def world_initialize():
@task
def wait_for_events() -> None:
"""Дождаться приёма всех событий описи, включая строки брака."""
"""Дождаться приёма всех событий паспорта, включая строки брака."""
inventory = json.loads(INVENTORY.read_text(encoding="utf-8"))
expected = sum(day["events"] for day in inventory["days"])
first_date = inventory["days"][0]["date"]