feat(stand): make up наполняет стенд стартовым миром, опись сторожит его
Зачем Стенд поднимался пустым, и всякая приёмка следующих этапов начиналась с ручной заливки данных. Теперь `make up` сам приводит стенд к одному и тому же состоянию, а в git лежит то, чем это состояние проверяется. Что - Опись мира `data/world-inventory.json`: паспорт (зерно, версия генератора, хеш каталога) и по строке на каждый из восьми дней — дата, число событий, хеш байтов. Собирается `make inventory`, свежесть сторожит `test_inventory.py` — тем же способом, что свежесть описания выгрузки. - Разовая служба `world-init` вышла из-под профиля и играет в топик восемь дней при каждом подъёме; зависимый у неё — `airflow-init`, иначе `--wait` считает успешно отработавшую службу упавшей. - `scripts/wait-for-world.sh` — вторая половина `make up`: приём асинхронный, поэтому ждать надо доезда до `ods.event`, а не завершения заливки. Ограниченный цикл опроса, не пауза наугад. - Девятая проверка `make check-clickhouse`: подневный счёт событий против описи, рамка по датам стартового мира, счёт через `FINAL`. При расхождении называет, где искать, — в событиях или в браке. - Порог «день ≤ 30 с» снят из спеки генератора в обоих местах: замер дал 1,7 с, порог был выше факта в восемнадцать раз. На его месте — замеры с датой. Раздел 9 спеки закрыт: открытых вопросов не осталось. - Слова: «манифест» стал описью мира, «зерновой мир» — стартовым миром (решение владельца). Оба заведены в словарь CONTEXT.md. Проверка `make clean && make up` с нуля — 2 м 50 с, доехало ровно 401 185 событий. `make check-clickhouse` зелёный (8 с), `make smoke` зелёный (9 с), `make test` — 407 тестов за 71 с, `make lint`, `make typecheck`, `make config-test` зелёные. Что проверка умеет краснеть, снято двумя поломками: снос партиции 2026-06-03 дал диагноз «не доехали до ODS», негодная строка в сырье — «сломан разбор». Строки опыта убраны, день переигран, счёт вернулся. Тест свежести проверен молчаливой правкой цены в каталоге: покраснел. Ссылка: #42
This commit is contained in:
1 parent
30a1e9e567
commit
7c9eeedc40
20 files changed
+625
-132
No files matched your search
@@ -31,14 +31,14 @@
|
||||
модельный день D — функция (зерно, D). Между прогонами живут только зерно
|
||||
и позиция на оси времени.
|
||||
- **Детерминизм до байта.** Одно зерно — побайтово тот же снимок; сверка —
|
||||
хешами манифеста. Транспорт (офсеты Kafka, темп) — вне обещания.
|
||||
хешами описи. Транспорт (офсеты Kafka, темп) — вне обещания.
|
||||
- **Схема — контракт генератора.** Python-модуль с чистыми данными;
|
||||
хранилище строится по рендеренной документации, границу сторожит строгий
|
||||
приём на стороне хранилища.
|
||||
- **Один сериализатор, глупые приёмники.** День-функция выдаёт канонические
|
||||
байты; приёмники — файл, Kafka пачкой, Kafka с темпом.
|
||||
- **Числа.** Средний день ~50 тыс. событий; эталонный снимок — 14 дней;
|
||||
в git — только манифест; автоматический порог один — день ≤ 30 с.
|
||||
в git — только опись мира; автоматических порогов по времени нет.
|
||||
|
||||
## 1. Модель мира
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
человеку предыстории, чьё окно активности таких дней не оставляет,
|
||||
пара не назначается. День-функция обязана назначенные заказы
|
||||
реализовать; остальные покупки — вольные, их решает генератор торговых
|
||||
событий (#40). Манифест считает пары, реализованные в горизонте
|
||||
событий (#40). Опись считает пары, реализованные в горизонте
|
||||
снимка. Условность в данных не видна: дни назначены той же
|
||||
случайностью, просто брошенной планом один раз.
|
||||
- **Своя ось модельного времени.** Ось событий начинается в
|
||||
@@ -117,19 +117,19 @@
|
||||
Отклонено с доводами:
|
||||
|
||||
- *Мутирующее состояние мира* («мир стареет»): ломает параллельность по
|
||||
дням, требует чекпоинтов, счётчики манифеста узнаваемы только постфактум;
|
||||
дням, требует чекпоинтов, счётчики описи узнаваемы только постфактум;
|
||||
ни один урок стенда на старении не стоит.
|
||||
- *Чистая функция без слоя состава*: глобальные инварианты пришлось бы
|
||||
выводить в каждом дне заново — тот же план мира, но неявный и размазанный.
|
||||
- *Привязка модельного времени к реальному календарю* (T-1 с догоном):
|
||||
конфликтует с ускорением ×60 — за вечер мир уезжает в будущее — и делает
|
||||
даты эталонного мира зависимыми от даты запуска, манифест теряет
|
||||
даты эталонного мира зависимыми от даты запуска, опись теряет
|
||||
воспроизводимость.
|
||||
|
||||
Отклонено при исполнении #38 (2026-08-02):
|
||||
|
||||
- *Разгон вместо предыстории* («первые дни малы — магазин запустился»):
|
||||
зерновой мир и половина снимка оказались бы на разгоне, недельная лаба
|
||||
стартовый мир и половина снимка оказались бы на разгоне, недельная лаба
|
||||
сравнивала бы несравнимые недели, «средний день ~50 тыс.» перестал бы
|
||||
быть средним — пришлось бы двигать принятые числа раздела 5.
|
||||
- *Материализованный план на горизонт*: горизонт становится обязательным
|
||||
@@ -139,7 +139,7 @@
|
||||
константы — потолка одновременно живущих кук, которого в жизни нет и
|
||||
который менти нечем объяснить.
|
||||
- *Вероятностная гарантия пар* («почти наверняка купит с обеих кук»): не
|
||||
гарантия — однажды манифест покраснеет, а чинить нечем, кроме смены
|
||||
гарантия — однажды опись покраснеет, а чинить нечем, кроме смены
|
||||
зерна; при этом несклеенная пара в данных неотличима от двух незнакомцев,
|
||||
так что реализм этой лотереи невидим.
|
||||
- *Покупка пары в первый визит куки*: гарантия железная и дешёвая, но узор
|
||||
@@ -154,14 +154,14 @@
|
||||
- **Обещание — содержимое до байта.** Два прогона с одним зерном дают тот же
|
||||
набор событий: те же `WatchID`/`VisitID`, поля, метки модельного времени.
|
||||
Снимок при пересборке побайтово совпадает: канонический порядок ключей и
|
||||
строк; сверка — по хешам манифеста, а два локально пересобранных снимка
|
||||
строк; сверка — по хешам описи, а два локально пересобранных снимка
|
||||
сравнимы обычным diff — пустой означает «ничего не изменилось». Вне
|
||||
обещания — транспорт: офсеты и партиции Kafka, какая нода прочитала,
|
||||
`_load_ts`, темп живого дня.
|
||||
- **Условия обещания.** Детерминизм держится при зафиксированном `uv.lock`
|
||||
и внутри канонического контейнера — то есть везде Linux, на маке и в WSL
|
||||
тоже; единственная переменная — архитектура CPU. Истина — CI на Linux; сходимость любой машины проверяет
|
||||
скрипт «пересгенерируй день N — сравни хеш с манифестом». Расхождение на
|
||||
скрипт «пересгенерируй день N — сравни хеш с описью». Расхождение на
|
||||
любой платформе — баг генератора, а не допуск.
|
||||
- **Раздача зерна — иерархией подпотоков.** Корневое зерно → подпоток
|
||||
состава мира, ветвящийся по номеру дня на когорты плана (состав
|
||||
@@ -173,7 +173,7 @@
|
||||
торговые события, расхождения, опоздания — в фиксированном порядке.
|
||||
По построению: параллельный прогон равен последовательному; продление
|
||||
истории днём N+1 не трогает дни 1…N; правка одного компонента меняет
|
||||
только его часть снимка — в манифесте меняются хеши только затронутых
|
||||
только его часть снимка — в описи меняются хеши только затронутых
|
||||
дней, дифф двух локальных пересборок читаем.
|
||||
- **Механизм подпотоков — `numpy.random.SeedSequence`.** Сверено через
|
||||
Context7 по документации numpy (2026-08-01): `spawn(n)` порождает детей
|
||||
@@ -188,7 +188,7 @@
|
||||
представление в клиентском `purchase` (урок мастер-спеки о расхождениях).
|
||||
- **Канонический seed и паспорт мира.** Эталонный мир собирается одним
|
||||
каноническим зерном — константой репозитория; свои зёрна менти крутит без
|
||||
гарантий манифеста. Манифест хранит паспорт мира — зерно и версию
|
||||
гарантий описи. Опись хранит паспорт мира — зерно и версию
|
||||
генератора; чек-скрипты сверяют паспорт раньше счётчиков.
|
||||
- **Суточный профиль интенсивности задаёт день-функция.** Форма — волны:
|
||||
ночной провал, обеденный и вечерний пики, различие будней и выходных;
|
||||
@@ -207,7 +207,7 @@
|
||||
- *Обещание детерминизма поверх обновления зависимостей*: numpy сознательно
|
||||
улучшает алгоритмы распределений между версиями (NEP 19), Faker меняет
|
||||
словари. Фиксация — `uv.lock`; обновление зависимостей — осознанная
|
||||
пересборка манифеста одним PR.
|
||||
пересборка описи одним PR.
|
||||
|
||||
## 3. Контракт схемы
|
||||
|
||||
@@ -255,7 +255,7 @@
|
||||
- **Один канонический сериализатор, глупые приёмники.** День-функция выдаёт
|
||||
упорядоченный поток канонических байтов — единственное место, где событие
|
||||
превращается в JSON. Приёмники не знают о содержимом: файл (локальный кэш
|
||||
для пересборки и проверок манифеста), Kafka пачкой — пакетный режим,
|
||||
для пересборки и проверок описи), Kafka пачкой — пакетный режим,
|
||||
Kafka с темпом ×60 — живой день. Новых топиков нет.
|
||||
- **Одно событие — одно сообщение Kafka.** «Пачкой» относится к темпу
|
||||
отправки, а не к упаковке: приёмник шлёт события подряд без пауз, но каждое
|
||||
@@ -288,7 +288,7 @@
|
||||
- **Рабочий выбор сериализатора — orjson**: быстрее stdlib json в 5–14 раз,
|
||||
numpy-массивы и datetime сериализует нативно (заметка исследования #31).
|
||||
Смена библиотеки меняет канонические байты, поэтому проходит как
|
||||
обновление зависимости: осознанная пересборка манифеста одним PR.
|
||||
обновление зависимости: осознанная пересборка описи одним PR.
|
||||
- **Промежуточные файлы не хранятся.** Файл дня — кэш чистой функции:
|
||||
потерял — пересчитал. В git снимок не попадает (раздел 5).
|
||||
- **Обрыв любого режима — переигровка дня целиком**; дедуп склеивает
|
||||
@@ -306,7 +306,7 @@
|
||||
|
||||
- *Отдельный топик / Kafka как хранилище дней*: офсеты и партиции вне
|
||||
обещания детерминизма, retention конечен, в git топик не положишь, хеш с
|
||||
манифестом не сверишь; Kafka на стенде — труба, не хранилище (раздел 7
|
||||
описью не сверишь; Kafka на стенде — труба, не хранилище (раздел 7
|
||||
мастер-спеки).
|
||||
- *Файл как обязательная станция доставки*: доигрывание обрыва уже решено
|
||||
через дедуп, канон держит единственный сериализатор, а не диск; файл
|
||||
@@ -327,15 +327,15 @@
|
||||
- **Эталонный снимок — 14 дней**: две полные календарные недели, D0 —
|
||||
понедельник. Самая короткая длина, при которой есть замороженная зона за
|
||||
окном K = 7, дышащая зона и две волны недельной сезонности. Удлинение до
|
||||
месяца — дешёвый ход (пересборка манифеста), если понадобится.
|
||||
- **В git — только манифест, снимок не хранится.** Снимок генерируется при
|
||||
месяца — дешёвый ход (пересборка описи), если понадобится.
|
||||
- **В git — только опись, снимок не хранится.** Снимок генерируется при
|
||||
`make up` и при проверках: артефакт — кэш чистой функции, кэш в git не
|
||||
хранят. Манифест несёт паспорт мира, счётчики и хеши по дням; проверки
|
||||
«пустой git diff» и «пересгенерируй день N — сравни хеш» живут на нём.
|
||||
хранят. Опись несёт паспорт мира, счётчики и хеши по дням; проверки
|
||||
«пустой git diff» и «пересгенерируй день N — сравни хеш» живут на ней.
|
||||
Каждый `make up` — живая демонстрация детерминизма. Честная потеря —
|
||||
страховка на случай платформенного бага: раньше менти с расходящимися
|
||||
байтами мог взять готовый снимок из git, теперь он упрётся в красный чек
|
||||
манифеста; смягчение — CI гоняет генерацию на amd64 и arm64.
|
||||
описи; смягчение — CI гоняет генерацию на amd64 и arm64.
|
||||
- **Живой день — ×60 по умолчанию**: модельные сутки за 24 реальные минуты,
|
||||
суточная волна разворачивается на глазах; темп в среднем ~35 событий/с,
|
||||
в пиковые часы сильных дней — до ~100. Число —
|
||||
@@ -348,34 +348,37 @@
|
||||
Схема двухъярусная — урок ADR 0004: пороги впритык к расчёту на разном
|
||||
железе кончаются ритуальным удалением проверки.
|
||||
|
||||
Ориентиры на референсной машине — в спеке, без автоматики:
|
||||
Ориентиры на референсной машине — в спеке, без автоматики. Где стоит замер,
|
||||
там он с датой; остальное — порядок величины, пока не мерили:
|
||||
|
||||
| Операция | Ориентир |
|
||||
|---|---|
|
||||
| Генерация одного дня | секунды |
|
||||
| Пересборка эталонного мира (14 дней + манифест, без транспорта) | до минуты |
|
||||
| Заливка снимка при `make up` (Kafka → матвью → ODS) | минуты |
|
||||
| Генерация одного дня | 1,7–2,8 с (замер 7 августа 2026 года) |
|
||||
| Пересборка эталонного мира (14 дней + опись, без транспорта) | до минуты |
|
||||
| Заливка стартового мира при `make up` (8 дней, Kafka → матвью → ODS) | 22,5 с (замер 7 августа 2026 года) |
|
||||
| Лаг живого дня | секунды |
|
||||
|
||||
**Автоматический порог один: полный день (50 тыс. событий) генерируется
|
||||
≤ 30 с.** Расчёт по планке исследования (~2–5×10⁵ событий/с на ядро) — доли
|
||||
секунды; порог держит машинный разброс ×2–5 и ловит деградацию на 1–2
|
||||
порядка: Faker в горячем цикле, случайная квадратичность. Реализация —
|
||||
pytest-тест с маркером `perf` и таймаутом-обрубанием: обязателен в CI,
|
||||
исключён из быстрой локальной петли, зовётся отдельной целью при правках
|
||||
горячего цикла.
|
||||
**Автоматических порогов нет ни одного** — решение владельца 7 августа
|
||||
2026 года при исполнении #42. Здесь стоял единственный: «полный день
|
||||
генерируется ≤ 30 с». Назначен он был до того, как генератор написали, а
|
||||
первый замер настоящего кода дал **1,7 секунды на день в 50 626 событий**
|
||||
(машина стенда, 7 августа 2026 года). Порог оказался в восемнадцать раз выше
|
||||
факта: зелен при любой правдоподобной регрессии, то есть не сторож, а
|
||||
украшение. Как здесь говорят о времени, к тому дню уже решила [карта
|
||||
целей](../architecture/testing.md): цена — замеренное число с датой, а не
|
||||
назначенный предел.
|
||||
|
||||
Остальное — наблюдаемость без порогов: `make up` и smoke печатают тайминги
|
||||
(генерация и доставка отдельно), проигрыватель логирует лаг. Прототип-замер
|
||||
до этапа 2 не нужен: числа назначены с запасом порядок и больше от планки
|
||||
исследования, планка подтверждена локальной проверкой на машине стенда;
|
||||
первый замер настоящего кода — порог этапа 2.
|
||||
Наблюдаемость без порогов остаётся и делает всю работу: проигрыватель
|
||||
печатает тайминги генерации и доставки раздельно, лаг живого дня — в его
|
||||
логе, а `make up` после #42 гоняет генератор по восемь дней при каждом
|
||||
подъёме стенда: замедлись он на порядок — подъём стенда встанет колом, и
|
||||
заметит это первый же человек, который его поднял.
|
||||
|
||||
Отклонено с доводами:
|
||||
|
||||
- *Четыре жёстких CI-ворот на все бюджеты*: машинный разброс против порогов
|
||||
впритык — повторение истории с памятью (ADR 0004); порог оставлен один,
|
||||
грубый, между «×5 шума» и «×100 беды».
|
||||
впритык — повторение истории с памятью (ADR 0004). Оставленный было один
|
||||
грубый порог снят при исполнении #42 (выше).
|
||||
- *Снимок в git* (статус-кво раздела 8 мастер-спеки): основание из v1 —
|
||||
медленный генератор — съедено детерминизмом и скоростью; остаётся только
|
||||
раздутый репозиторий. *Снимок вложением релиза Gitea*: страховка без
|
||||
@@ -411,9 +414,9 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
- **Раздел 1.4**: «из контракта выводятся DDL и валидация» заменено на data
|
||||
contract — хранилище пишется по документации, границу сторожит строгий
|
||||
приём (раздел 3 здесь).
|
||||
- **Раздел 8**: артефакт `data/startup_history/` в git заменён манифестом;
|
||||
- **Раздел 8**: артефакт `data/startup_history/` в git заменён описью;
|
||||
снимок генерируется на месте (раздел 5 здесь). Туман «политика
|
||||
версионирования артефакта» закрыт этим же ходом: версионируется манифест.
|
||||
версионирования артефакта» закрыт этим же ходом: версионируется опись.
|
||||
- **Раздел 11**: пункт «до этапа 3 зафиксировать требования
|
||||
производительности» закрыт числами раздела 5.
|
||||
- Мелкие согласования там, где текст опирался на артефакт в git: источник
|
||||
@@ -444,7 +447,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
по расписанию (~раз в 24 минуты); генератор не дорабатывается.
|
||||
- **Этап 5, позиция на оси времени.** Проигрыватель состояния не хранит
|
||||
(раздел 9), поэтому вести позицию — работа того, кто его зовёт. Живёт она
|
||||
переменной Airflow: отсутствие переменной означает мир в зерновом
|
||||
переменной Airflow: отсутствие переменной означает мир в стартовом
|
||||
состоянии, заводит и двигает её только даг `next_day` и только по успеху.
|
||||
Довод — генератор отдельная и заменяемая сущность, привязывать его к
|
||||
хранилищу незачем, а `make clean` сносит том метаданных Airflow вместе с
|
||||
@@ -452,7 +455,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
Расхождение переменной с данными (менти почистил партицию руками) лечится
|
||||
документацией, а не сторожем: постоянная сверка была бы проверкой, красной
|
||||
в норме ([ADR 0002](../adr/0002-monitoring-scope.md)).
|
||||
- **Этап 7 (эталонный мир)**: пересборка — это манифест, не артефакт;
|
||||
- **Этап 7 (эталонный мир)**: пересборка — это опись, не артефакт;
|
||||
CI-генерация на amd64 и arm64.
|
||||
- **Будущие лабы**: перезаливка дня X пакетным режимом проигрывателя —
|
||||
готовая демонстрация идемпотентности конвейера.
|
||||
@@ -503,11 +506,11 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
возвратами предыстории), 170 пар.
|
||||
- **Конфигурация мира — модуль чистых данных** рядом с контрактом схемы:
|
||||
все числа мира в одном месте, написанном как приглашение любопытному
|
||||
менти крутить. Правка модуля — смена мира: чек манифеста честно
|
||||
краснеет, манифест сторожит только канон. Вне модуля — лишь то, что
|
||||
менти крутить. Правка модуля — смена мира: чек описи честно
|
||||
краснеет, опись сторожит только канон. Вне модуля — лишь то, что
|
||||
мира не меняет: своё зерно и транспортные флаги проигрывателя.
|
||||
Отклонено: внешний конфиг и env-переопределения — переменная мира,
|
||||
которую паспорт манифеста не видит; файл-конфиг в репозитории — по
|
||||
которую паспорт описи не видит; файл-конфиг в репозитории — по
|
||||
смыслу равен модулю, но платит загрузчиком и валидацией (довод
|
||||
раздела 3 против YAML).
|
||||
|
||||
@@ -590,7 +593,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
посетителю 6% до корзины и те же 45% и 55%, помеченному покупателю — 18%
|
||||
и 60%. Перемеренный средний день: 9 879 визитов, 47 433 pageview, 4,80
|
||||
страницы на визит, конверсия визита 2,4%; полные числа — в блоке #40,
|
||||
оттуда же они лягут в манифест.
|
||||
оттуда же они лягут в опись.
|
||||
- **Недельная волна применяется один раз.** Профиль ведёт приток, трафик
|
||||
наследует его через дневную аудиторию; измеренный размах трафика — ±7%
|
||||
против ±10% у притока. Второе умножение удвоило бы недельный размах. Если
|
||||
@@ -611,8 +614,8 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
|
||||
Решено при исполнении #40 (2026-08-02) — решения владельца до реализации.
|
||||
Три решения выходят за границы тикета: правятся план состава (#38), воронка
|
||||
(#39) и каталог товаров (#39). Сейчас это дёшево — манифест ещё не собран
|
||||
(#42); позже обошлось бы его пересборкой. Часть решений принята после
|
||||
(#39) и каталог товаров (#39). Сейчас это дёшево — опись ещё не собрана
|
||||
(#42); позже обошлось бы её пересборкой. Часть решений принята после
|
||||
холодного ревью первой редакции блока.
|
||||
|
||||
- **Корзина шире заказа.** Посетитель кладёт в корзину товары тех карточек,
|
||||
@@ -872,7 +875,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
цены и по медиане, потому что одни только края списка обманываются
|
||||
четырьмя исключениями. Хвост соседям: словарь ClickHouse над этим файлом
|
||||
ещё не построен (#37/#43) — колонку надо взять в его описание сразу, а не
|
||||
догонять правкой; манифесту нужен хеш каталога (хвост #42) тем более,
|
||||
догонять правкой; описи нужен хеш каталога (хвост #42) тем более,
|
||||
потому что теперь в файле живёт ещё и поведение.
|
||||
- **Доля просмотров карточек, доходящих до корзины, выросла с 7,3% до 8,6%
|
||||
и подошла к потолку вилки.** Иначе не сошлось: треть кладущих визитов вне
|
||||
@@ -885,7 +888,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
пока число правдоподобно, само по себе оно ничего не сторожит, и
|
||||
подгонять поведение под его край не надо. Настоящий предел здесь другой и
|
||||
считается в другой валюте — дневной бюджет событий: события корзины
|
||||
входят в те самые «около 50 тыс.», на которых стоят манифест (#42) и
|
||||
входят в те самые «около 50 тыс.», на которых стоят опись (#42) и
|
||||
порог скорости дня. Упрётся будущая правка — двигать надо бюджет и его
|
||||
причины, а не долю.
|
||||
|
||||
@@ -926,7 +929,7 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
итог виден кодом возврата. Общее у режимов: `--seed`, `--day`, `--days` и
|
||||
приёмник — `--file` либо `--brokers` с `--topic`. Состояния нет: позицию на
|
||||
оси ведёт зовущий (хвост этапу 5 — раздел 8). `--days` играет несколько
|
||||
дней подряд одним запуском — этим зальётся зерновой мир (#42), восемь
|
||||
дней подряд одним запуском — этим зальётся стартовый мир (#42), восемь
|
||||
запусков службы внутри `make up` были бы плохим ответом. Отклонено:
|
||||
*режимы флагом `--speed 0`* — различие тогда прячется за числом, тогда как
|
||||
у режимов разное устройство: у пакетного есть пачка и нет ожидания, у
|
||||
@@ -986,11 +989,30 @@ pytest-тест с маркером `perf` и таймаутом-обрубан
|
||||
модуль его не находит. Проверено запуском в контейнере; заодно день,
|
||||
сыгранный в образе, совпал побайтово с днём, сыгранным на машине.
|
||||
|
||||
Остаётся открытым, за тикетами:
|
||||
Решено при исполнении #42 (2026-08-07):
|
||||
|
||||
- как фиксируется «зерновой» мир конца этапа 2 (раздел 9 мастер-спеки):
|
||||
с манифестным решением напрашивается мини-манифест зернового мира —
|
||||
форма за #42;
|
||||
- паспорт мира в манифесте (зерно и версия генератора) файл каталога не
|
||||
накрывает: правка цены в CSV меняет мир молча. Манифесту нужен хеш
|
||||
каталога — хвост для #42.
|
||||
- **Опись мира одна, и она же растёт до эталонной.** Стартовый мир конца
|
||||
этапа 2 — первые восемь дней оси, понедельник по понедельник (решение
|
||||
владельца при нарезке, 2026-08-01): полная неделя с выходными и первый
|
||||
замкнутый цикл окна K = 7. Отдельного «младшего» файла для них не
|
||||
заводится: `data/world-inventory.json` — та самая опись, которую раздел 5
|
||||
обещает эталонному миру, пока короткая. Этап 7 продлит её до четырнадцати
|
||||
дней, а не заведёт вторую. Форма — JSON: паспорт мира (зерно, версия
|
||||
генератора, хеш каталога) и по строке на день с датой, числом событий и
|
||||
хешем. Собирается целью `make inventory`, свежесть сторожит тест
|
||||
`test_inventory.py` — тем же способом, что свежесть «описания выгрузки».
|
||||
Отклонено: *два файла, «мини» и полный* — две правды об одном мире и
|
||||
лишнее слово в словаре.
|
||||
- **Хеш каталога — в паспорте, и работа у него объяснительная.** Правка
|
||||
цены в `data/catalog/products.csv` меняет мир так же молча, как правка
|
||||
кода, но поймают её и без хеша: хеши дней разойдутся. Хеш каталога
|
||||
отвечает на следующий вопрос — **что** правили: разошлись дни и каталог —
|
||||
CSV; разошлись только дни — код.
|
||||
- **Слова.** «Манифест» по всей спеке переименован в **опись мира**, а
|
||||
«зерновой мир» — в **стартовый мир** (решение владельца 2026-08-07).
|
||||
Довод — правило языка репозитория: есть обычное русское слово — берётся
|
||||
оно. Заодно исчезла пара «манифест / мини-манифест», в которой читателю
|
||||
пришлось бы различать две сущности там, где вещь одна.
|
||||
|
||||
Открытых вопросов за разделом не осталось: интерфейс запуска закрыт #41,
|
||||
форма описи и хеш каталога — здесь.
|
||||
Reference in new issue
Block a user