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>
This commit is contained in:
ddadminandClaude Fable 5 committed 2026-08-30 15:53:52 -04:00
1 parent f93c924b33
commit c1b61d81b9
8 files changed
+88 -38

No files matched your search

+26 -5
View File
@@ -36,7 +36,7 @@
Стенд нужен трём целям из восьми. Цена — замер, см. «Что проверено».
`make smoke` и `make check-services` перемерены 28 августа 2026 года,
`make config-test` — 21 августа, `make check-clickhouse` — 19 августа,
корневой `make lint` — 9 августа, `make test` — 7 августа; остальные стоят с
корневой `make lint` — 9 августа, `make test` — 30 августа; остальные стоят с
замера 6 августа.
| Цель | Откуда | Что утверждает | Стенд | Цена |
@@ -48,7 +48,7 @@
| `make check-services` | корень | **Службы работают**: DAG запускается и доходит, топик создаётся и удаляется, Superset логинится и ходит в базу | нужен | 71 с |
| `make lint` | `generator/` | Код генератора отформатирован и проходит ruff | не нужен | 0,4 с |
| `make typecheck` | `generator/` | Типы генератора сходятся (ty) | не нужен | 0,5 с |
| `make test` | `generator/` | Генератор делает то, что обещает; схема события остаётся объявленным контрактом, а собранные из кода [описание выгрузки](../formats/clickstream-event.md) и [опись мира](../../data/world-inventory.json) — свежими | не нужен | 71 с |
| `make test` | `generator/` | Генератор делает то, что обещает; схема события остаётся объявленным контрактом, а собранные из кода [описание выгрузки](../formats/clickstream-event.md) и [опись мира](../../data/world-inventory.json) — свежими | не нужен | 176,5 с |
**Строк с именем `make lint` две, и это не опечатка.** Одноимённые цели за
разными дверями охватывают разное: корневая не видит ни одного файла
@@ -121,9 +121,9 @@ ClickHouse отвечает сразу.
из каталога `generator/`: стенд им не нужен, а `make test` из них самая
дорогая. Правки дагов и Superset — корневой `make lint`.
Тринадцать секунд из её семидесяти одной — пересборка восьми модельных дней
для сверки с описью мира. Дешевле хеши не сравнить: чтобы узнать, тот ли
получается мир, его надо получить. Место выбрано по той же оси «кого
Двадцать секунд из ее ста семидесяти шести — пересборка восьми модельных дней
для сверки с описью мира (20,5 с из 176,5). Дешевле хеши не сравнить: чтобы
узнать, тот ли получается мир, его надо получить. Место выбрано по той же оси «кого
спрашивают» — вопрос обращён к коду генератора, стенд ему не нужен, — и
краснеет проверка там, где надо: сразу после правки генератора. Не будь её,
расхождение всплыло бы получасом позже, на поднятом стенде, где выглядит
@@ -238,6 +238,27 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
## Что проверено
**Перезамер 30 августа 2026 года при исполнении #192.** Команды запускались
из `generator/`:
```bash
time make inventory
uv run python -m clickstream_generator batch --day 0 --days 8 \
--file /tmp/stage7-events.jsonl
for snapshot_day in 1 2 3 4 5 6 7; do
uv run python -m clickstream_generator snapshot --day "$snapshot_day" \
--file "/tmp/stage7-snapshot-${snapshot_day}.jsonl"
done
wc -c /tmp/stage7-events.jsonl /tmp/stage7-snapshot-{1,2,3,4,5,6,7}.jsonl
time make test
```
Опись пересобралась за 20,515 с. Файловый приемник записал 460 731 812 байт:
458 478 771 байт событий и 2 253 041 байт слепков. `make test` прошел 437/0
за 176,476 с. Прежние 71 с (7 августа, #42) выросли по двум причинам: тестов
стало 437 против 407, и потяжелел сам мир — та же пересборка восьми дней
стоила 13 с, теперь 20,5. Эта правка добавила один тест — проигрыш дня.
**Приемка #172 28 августа 2026 года.** Холодный `create_app()` занимал
28,3-28,4 секунды, а чтение подключения после запуска - 0,07 секунды. Предел
30 секунд почти целиком уходил на запуск второго приложения. Увеличивать его
+7 -6
View File
@@ -532,10 +532,11 @@ Kafka день переигрывается генератором заново:
## 8. Эталонный мир и опись
В git хранится только опись эталонного мира; сам снимок (14 модельных
дней) генерируется на месте — при `make up` и при проверках (решение
развилки «Производительность» карты #26, подробности —
[спека генератора](2026-08-01-generator.md), раздел 5). Опись несёт
В git хранится только опись эталонного мира; сам снимок генерируется на
месте — при `make up` и при проверках (решение развилки «Производительность»
карты #26, подробности — [спека генератора](2026-08-01-generator.md),
раздел 5). Снимок — 8 дней, совпадает со стартовым миром: обещание в 14 дней
снято на этапе 7. Опись несёт
паспорт мира (каноническое зерно, версия генератора), контрольные счётчики
и хеши по дням; проверки «пустой git diff» и «пересгенерируй день N —
сравни хеш» живут на ней. Политика версионирования артефакта (бывший туман
@@ -555,8 +556,8 @@ Kafka день переигрывается генератором заново:
названного читателя в опись не кладётся
([что хранит опись](../architecture/orders/inventory.md)).
Снимок вырастет против v1 (ecommerce-массивы, заказы) — размер проверить
при пересборке.
Размер снимка замерен на этапе 7 — запись в разделе «Что проверено»
[карты проверок](../architecture/testing.md).
## 9. Оценка объёма исполнения
+20 -10
View File
@@ -65,7 +65,8 @@
приём на стороне хранилища.
- **Один сериализатор, глупые приёмники.** День-функция выдаёт канонические
байты; приёмники — файл, Kafka пачкой, Kafka с темпом.
- **Числа.** Средний день ~50 тыс. событий; эталонный снимок — 14 дней;
- **Числа.** Средний день ~50 тыс. событий; эталонный снимок совпадает со
стартовым миром — 8 дней;
в git — только опись мира; автоматических порогов по времени нет.
## 1. Модель мира
@@ -358,10 +359,17 @@
- **Средний модельный день — ~50 тыс. событий**; суточные волны с пиками до
~2× среднего, будни/выходные; ≈8–12 тыс. сессий, 6–8 тыс. посетителей —
правдоподобный средний магазин.
- **Эталонный снимок — 14 дней**: две полные календарные недели, D0 —
понедельник. Самая короткая длина, при которой есть замороженная зона за
окном K = 7, дышащая зона и две волны недельной сезонности. Удлинение до
месяца — дешёвый ход (пересборка описи), если понадобится.
- **Эталонный снимок совпадает со стартовым миром — 8 дней**: понедельник
по понедельник, полная неделя и один закрытый день за окном K = 7.
Обещание в 14 дней снято на этапе 7 (решение владельца 2026-08-30):
у дней 8–13 не нашлось читателя — лабы проигрывают мир дальше без описи,
транспорт байты с описью не сверяет, а проверке «пересгенерируй день N —
сравни хеш» день 3 годится так же, как день 13. Замеры и тесты, меряющие
мир на окне в две недели, не меняются: мир — функция без края, окну
замера опись не нужна. Удлинение описи остается дешевым ходом
(пересборка) и делается, когда читатель появится — например, если
растяжке `classes_vs_inventory` понадобится сверять больше одного
закрытого дня.
- **В git — только опись, снимок не хранится.** Снимок генерируется при
создании мира и в проверках генератора: артефакт — кэш чистой функции, кэш в
git не хранят. Опись несёт паспорт мира, счётчики и хеши по дням; проверки
@@ -389,7 +397,7 @@
| Операция | Ориентир |
|---|---|
| Генерация одного дня | 1,7–2,8 с (замер 7 августа 2026 года) |
| Пересборка эталонного мира (14 дней + опись, без транспорта) | до минуты |
| Пересборка эталонного мира (8 дней + опись, без транспорта) | 20,5 с (замер 30 августа 2026 года) |
| Заливка стартового мира при `make up` (8 дней, Kafka → матвью → ODS) | 22,5 с (замер 7 августа 2026 года) |
| Лаг живого дня | секунды |
@@ -925,11 +933,13 @@
### Опись мира и словарь
- **Опись мира одна и растет до эталонной** (#42, 2026-08-07; решение владельца
при нарезке, 2026-08-01). Стартовый мир этапа 2 — первые восемь дней,
- **Опись мира одна** (#42, 2026-08-07; эталонная длина — 8 дней, этап 7,
2026-08-30). Стартовый мир этапа 2 — первые восемь дней,
понедельник по понедельник: полная неделя и первый замкнутый цикл K = 7.
`data/world-inventory.json` — та самая опись, которую обещает раздел 5, пока
короткая; этап 7 продлит ее до 14 дней, а не заведет вторую. В JSON лежат
`data/world-inventory.json` — та самая опись, которую обещает раздел 5.
Этап 7 оставил ее восьмидневной: у хвоста в шесть дней не нашлось читателя
(резолюция — в разделе 5); вырастет она продолжением этого же файла, а не
вторым. В JSON лежат
паспорт мира — зерно, версия генератора и хеш каталога — и строки дней с
датой, числом событий и хешем. Опись собирает `make inventory`, свежесть
сторожит `test_inventory.py` тем же способом, что свежесть описания выгрузки.