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

+3 -3
View File
@@ -64,8 +64,8 @@
- `src/clickstream_generator/schema_doc.py` — сборка «описания выгрузки»
([`docs/formats/clickstream-event.md`](../docs/formats/clickstream-event.md))
из контракта. Документ руками не правят — пересобирают.
- `src/clickstream_generator/inventory.py` — сборка описи мира
([`data/world-inventory.json`](../data/world-inventory.json)): паспорт мира
- `src/clickstream_generator/inventory.py` — сборка паспорта мира
([`data/world-inventory.json`](../data/world-inventory.json)): шапка паспорта
и хеши восьми дней, которыми наполняется стенд, плюс счётчики классов
расхождений по заказам для дней с закрытым окном. Руками не правят —
пересобирают целью `make inventory`.
@@ -88,7 +88,7 @@
- `make typecheck` — ty: проверка типов;
- `make demo` — пройти стадии канонического D0 на одном экране;
- `make docs` — пересобрать описание выгрузки;
- `make inventory` — пересобрать опись мира.
- `make inventory` — пересобрать паспорт мира.
В корне репозитория `make lint` тоже есть, но он про код стенда — даги и
Superset. Одноимённые цели за разными дверями охватывают разное:
@@ -1,15 +1,15 @@
"""Опись мира: чем стенд наполняется при подъёме и каким это обязано выйти.
"""Паспорт мира: чем стенд наполняется при подъёме и каким это обязано выйти.
Мир — чистая функция зерна (спека генератора, раздел 2), поэтому в git лежит не
он сам, а опись: паспорт мира, число событий и хеш байтов каждого дня, счётчики
классов расхождений по заказам. Сам мир пересчитывается когда угодно, а опись
он сам, а паспорт: шапка, число событий и хеш байтов каждого дня, счётчики
классов расхождений по заказам. Сам мир пересчитывается когда угодно, а паспорт
отвечает на единственный вопрос — **тот ли это мир, что был вчера**. Разошлись
хеши — мир уехал, и дальше уже неважно, чего от него ждали проверки.
Дней в описи восемь: столько заливается в стенд при `make up`. Понедельник по
Дней в паспорте восемь: столько заливается в стенд при `make up`. Понедельник по
понедельник — полная неделя с выходными и первый замкнутый цикл окна K = 7.
Это и есть эталонный мир: обещание в четырнадцать дней снято на этапе 7 —
у хвоста не нашлось читателя. Понадобится длиннее — опись продолжит этот
у хвоста не нашлось читателя. Понадобится длиннее — паспорт продолжит этот
же файл, а не заведет второй.
**Сторожат мир хеши, а не паспорт.** Паспорт отвечает на другой вопрос — «чем
@@ -30,9 +30,9 @@
Слепок заказов — второй артефакт мира, и хешами дней он не покрыт ни при каком
раскладе подпотоков, поэтому у каждого отправленного слепка своя строка. Их на
один меньше, чем дней: прогон дня 0 отправлять ещё нечего. Счёта строк у
слепка нет — опись описывает мир, а не доставку.
слепка нет — паспорт описывает мир, а не доставку.
Собирается опись из каталога генератора целью `make inventory`, а свежесть её
Собирается паспорт из каталога генератора целью `make inventory`, а его свежесть
сторожит тест — как и у «описания выгрузки».
"""
@@ -57,7 +57,7 @@ STARTING_DAYS = 8
def build() -> dict[str, Any]:
"""Опись целиком: паспорт мира, строка на день и строка на слепок.
"""Паспорт целиком: шапка, строка на день и строка на слепок.
Дни играются по одному и отпускаются: заказы дня остаются, потому что из
них собираются слепки, а полсотни тысяч событий восьми дней сразу в память
@@ -83,12 +83,12 @@ def build() -> dict[str, Any]:
def render() -> str:
"""Опись текстом файла: отступы в два пробела, кириллица как есть."""
"""Паспорт текстом файла: отступы в два пробела, кириллица как есть."""
return json.dumps(build(), ensure_ascii=False, indent=2) + "\n"
def _day(number: int, today: day_module.Day) -> dict[str, Any]:
"""Строка описи: номер дня, его дата, число событий и хеш байтов.
"""Строка паспорта: номер дня, его дата, число событий и хеш байтов.
Дата считается от D0 арифметикой, а не берётся из событий: ось модельного
времени так и определена (`world.ORIGIN`), и по этой же дате счётчики
@@ -105,7 +105,7 @@ def _day(number: int, today: day_module.Day) -> dict[str, Any]:
def _snapshot(number: int, orders: list[orders_module.Orders]) -> dict[str, Any]:
"""Строка описи слепка: какой день снят, его дата и хеш отправленных байтов.
"""Строка паспорта слепка: какой день снят, его дата и хеш отправленных байтов.
Байты те же, что уезжают в топик `orders`, с переводом строки после
каждого заказа: пересъёмка слепка обязана дать их снова.
@@ -169,12 +169,12 @@ def _digest(payload: bytes) -> str:
def main() -> None:
parser = argparse.ArgumentParser(
description="Собирает опись мира: паспорт, счётчики и хеши дней."
description="Собирает паспорт мира: шапку, счётчики и хеши дней."
)
parser.add_argument("output", type=Path, help="путь к файлу описи")
parser.add_argument("output", type=Path, help="путь к файлу паспорта")
output = parser.parse_args().output
output.write_text(render(), encoding="utf-8")
print(f"Опись мира собрана: {output}")
print(f"Паспорт мира собран: {output}")
if __name__ == "__main__":
@@ -15,7 +15,7 @@
Тайминги печатаются раздельно — генерация и доставка, как требует спека
(раздел 5): это разные машины разной природы, и сложенные в одно число они
перестают что-либо говорить. Сериализация считается частью генерации: она
рождает те самые байты, которые сторожит опись.
рождает те самые байты, которые сторожит паспорт.
**Слепки заказов идут третьим ходом того же проигрывателя** и живут по правилу
сдвига: прогон дня D отправляет слепок дня D−1 — ночная выгрузка бэкенда за
+2 -2
View File
@@ -25,8 +25,8 @@ from enum import IntEnum
import numpy as np
# Каноническое зерно эталонного мира — константа репозитория; опись хранит
# его в паспорте мира. Свои зёрна менти крутит без гарантий описи.
# Каноническое зерно эталонного мира — константа репозитория; паспорт хранит
# его в своей шапке. Свои зёрна менти крутит без гарантий паспорта.
CANONICAL_SEED = 20260601
@@ -18,7 +18,7 @@
строгий приём хранилища сверяет набор ключей (ADR 0005).
- **Даты и время — ISO-8601** (спека, раздел 4): `EventDate` уезжает как
`2026-06-01`, `UTCEventTime` — как `2026-06-01T12:34:56Z`. Довод — читаемость
сырья: менти открывает колонку `raw` обычным клиентом и разбирает событие
сырых данных: менти открывает колонку `raw` обычным клиентом и разбирает событие
глазами, а число эпохи этот урок убивает.
- **Одно событие — один документ JSON**, без перевода строки внутри: приёмник
сам решает, чем их разделить.
+3 -3
View File
@@ -2,8 +2,8 @@
Модуль — приглашение крутить: поменяйте число, пересоберите снимок и
посмотрите, что стало с данными. Правка любой константы здесь — смена мира,
поэтому чек описи честно покраснеет: опись сторожит только канонический мир,
свои миры менти собирает без её гарантий (спека генератора, раздел 9).
поэтому чек паспорта честно покраснеет: паспорт сторожит только канонический мир,
свои миры менти собирает без его гарантий (спека генератора, раздел 9).
Числа решены спекой и связаны между собой; связки сторожат тесты
`test_world.py`, чтобы правка одного числа не рассыпала вывод соседнего.
@@ -28,7 +28,7 @@ COUNTER_TIMEZONE_MINUTES = 240 # Europe/Samara
# D0 — первый день оси модельного времени, понедельник. Реальный календарь в
# модели не участвует: дата нужна лишь затем, чтобы дни оси легли в
# `EventDate`/`UTCEventTime` конкретными числами. От даты запуска мир не
# зависит — иначе опись перестала бы быть воспроизводимой.
# зависит — иначе паспорт перестал бы быть воспроизводимым.
ORIGIN = date(2026, 6, 1)
# Окно изменяемости заказа, модельные дни: слепок бэкенда несёт заказы,
+1 -1
View File
@@ -5,7 +5,7 @@ from pathlib import Path
def test_demo_runs_and_shows_the_whole_chain() -> None:
"""Сторожит команду и стадии, а числа мира оставляет описи."""
"""Сторожит команду и стадии, а проверку чисел мира оставляет паспорту."""
finished = subprocess.run(
["make", "--silent", "demo"],
check=False,
+9 -9
View File
@@ -1,7 +1,7 @@
"""Проверка описи мира: та ли она, что собирается из кода сегодня.
"""Проверка паспорта мира: совпадает ли он с тем, что собирается из кода сегодня.
Опись собирается из кода, значит разойтись они могут только одним способом —
код правили, опись не пересобрали. Ровно это здесь и сторожится, тем же
Паспорт собирается из кода, значит разойтись они могут только одним способом —
код правили, паспорт не пересобрали. Ровно это здесь и сторожится, тем же
способом, что свежесть «описания выгрузки».
Проверка дорогая — она пересчитывает восемь модельных дней целиком, и это
@@ -23,24 +23,24 @@ from clickstream_generator.seeds import CANONICAL_SEED
INVENTORY_PATH = Path(__file__).resolve().parents[2] / "data" / "world-inventory.json"
# Прогон, слепок которого сверяется с описью байт в байт. День любой из
# Прогон, слепок которого сверяется с паспортом байт в байт. День любой из
# отправляемых; этот дешевле прочих — его окно короче.
RUN_DAY = 2
# День любой: хеш дня не зависит ни от соседей, ни от края описи.
# День любой: хеш дня не зависит ни от соседей, ни от края паспорта.
REPLAY_DAY = 4
@pytest.fixture(scope="module")
def inventory() -> dict:
"""Опись, собранная из кода: мир пересчитывается один раз на весь модуль."""
"""Паспорт, собранный из кода: мир пересчитывается один раз на весь модуль."""
return build()
def test_inventory_is_up_to_date(inventory: dict):
stored = json.loads(INVENTORY_PATH.read_text(encoding="utf-8"))
assert stored == inventory, (
"опись мира отстала от кода — пересоберите: make inventory."
"паспорт мира отстал от кода — пересоберите: make inventory."
" Разошлись хеши дней и каталога — правили data/catalog/products.csv;"
" разошлись только дни — правили генератор"
)
@@ -68,7 +68,7 @@ def test_the_inventory_holds_the_hash_of_every_sent_snapshot(inventory: dict, tm
"""У каждого отправленного слепка — своя строка с хешем его байтов.
Байты слепка не покрыты хешами дней ни при каком раскладе подпотоков: это
второй артефакт мира, и побайтовое обещание сторожит опись. Слепков на день
второй артефакт мира, и побайтовое обещание сторожит паспорт. Слепков на день
меньше, чем дней: прогон дня 0 не отправляет ничего.
Хеш сверяется с настоящей выгрузкой, а не с самим собой: в файл уезжают те
@@ -86,7 +86,7 @@ def test_the_inventory_holds_the_hash_of_every_sent_snapshot(inventory: dict, tm
def test_a_replayed_day_matches_its_inventory_hash(tmp_path):
"""Опись сверяется с байтами, вышедшими из CLI, а не с собранной тут же:
"""Паспорт сверяется с байтами, вышедшими из CLI, а не с собранной тут же:
пересчет сравнивался бы сам с собой.
"""
stored = json.loads(INVENTORY_PATH.read_text(encoding="utf-8"))
+1 -1
View File
@@ -28,7 +28,7 @@ def test_every_event_carries_every_column(events):
«Пусто» по контракту — пустое значение, а не отсутствие ключа: пропавший
ключ уводит событие в брак целиком (ADR 0005). Порядок ключей — часть
канона: от него зависят байты, а значит и хеши описи.
канона: от него зависят байты, а значит и хеши паспорта.
"""
names = [column.name for column in schema.COLUMNS]
for event in events:
+1 -1
View File
@@ -167,7 +167,7 @@ def test_the_moments_carry_real_milliseconds(days: list[day_module.Day]):
def test_the_rows_go_in_the_order_of_birth(days: list[day_module.Day]):
"""Порядок строк слепка — порядок рождения заказов, он же рост номера.
Хешу слепка в описи нужен именно названный порядок: детерминизм даёт его
Хешу слепка в паспорте нужен именно названный порядок: детерминизм даёт его
даром, но обещание побайтового повтора держится на нём, а не на удаче.
"""
numbers = [record["order_id"] for record in snapshot_records(days, FULL_WINDOW_DAY)]