docs(orders): горячее ревью черновика спеки

Зачем: перед вычитанием #84 черновик перечитан глазами читателя против
резолюций — текстовые огрехи сборки правятся сейчас, пока дёшево.

Что: возвращено правило «опоздание меряется от снятия слепка, не отправки»
(#71/#72); «назначенное планом не портится» сужено до честного «не теряется
и не опаздывает»; created_at в опоздании описан без слова «честный»;
уточнено, что слепки world-init отправляет команда snapshot; в §2 добавлены
пропущенные отклонённые варианты; поправлены ссылки на разделы спеки
генератора; в перечислении компонентов дня стороны названы своими
предметами.

Проверка: чтение; вычитание — #84.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 22:21:12 +03:00
co-authored by Claude Fable 5
parent 815f500127
commit a2256a6210
2 changed files with 29 additions and 18 deletions
+24 -14
View File
@@ -70,10 +70,11 @@
в трекере — не отдельная порода, а класс B, и делает его событийная сторона
выбрасыванием события после присвоения номера (раздел 4);
- номер заказа общий у обеих проекций: `order_id` = клиентский `purchaseID`,
читаемый номер «день и порядковый номер покупки» (спека генератора, блок
#40); нумеруются все покупки, дошедшие до потока дня, — до всяких потерь;
читаемый номер «день и порядковый номер покупки» (спека генератора,
раздел 9); нумеруются все покупки, дошедшие до потока дня, — до всяких
потерь;
- скидка заказа выводится из промокода события по таблице «код → скидка» —
числу мира, которое этап 3 берёт готовым (спека генератора, раздел 8).
числу мира, которое этап 3 берёт готовым (спека генератора, разделы 8 и 9).
В модели строка заказа в базе источника создаётся синхронно с покупкой, поэтому
день рождения заказа и день создания строки совпадают. Смысл полей от этого не
@@ -96,7 +97,10 @@
глазами как два производителя, каждый со своим топиком. Один прогон с двумя
выходами отклонён: экономии он не даёт (со сдвигом отправки окно слепка и
сыгранный день не пересекаются вовсе), а правило «приёмник выбирается тем, что
для него назвали» ломает.
для него назвали» ломает. Отклонены также: *отдельный пакет и образ*
библиотека на двоих ради одной команды; *генератор пишет слепок файлом, в
топик льёт даг* — второй путь доставки и второй сериализатор; *слепок едет
топиком `hits`* — убивает два режима приёма.
**Сборка окна.** Слепок дня D несёт заказы, рождённые в дни D−6…D, и собирается
переигровкой этих семи дней: заказы дня — производная всей воронки дня,
@@ -231,7 +235,7 @@
класса — разрыв воронки, на котором держится потеря, сжался бы втрое; при
сюжетном разрыв снова равен доле потерь. `purchaseID`, суммы и позиции у дубля
один в один — посчитал наивно, удвоил выручку. Дубль случается только там, где
до следующего визита куки есть место больше таймаута: иначе сборка сессий у
до следующего визита куки остаётся запас сверх таймаута: иначе сборка сессий у
менти разошлась бы с `VisitID` — сломался бы эталон, ради которого `VisitID`
в потоке лежит. Задержка дубля — секунды-минуты, короче таймаута визита,
поэтому `VisitID` тот же. Полночь режет дубль парой — просмотр вместе с
@@ -247,12 +251,15 @@
не расхождение в данных; менти различить не может. Гарантия плана старше
расхождения.
**Опоздание — заказ прячется от ранних слепков.** `created_at` честный, но в
слепках дней d…d+δ−1 строки нет, а с d+δ она появляется в том состоянии, до
которого заказ дожил: отменённый на второй день и опоздавший на третий приедет
в первом же своём слепке как `cancelled` — «выгрузка догоняет жизнь». Задержка
— таблица весов из трёх строк: 0 на подавляющем весе, 1 и 2 — это и есть
«D+1/D+2» мастер-спеки. Дальше таблица не идёт: заказ с δ = 6 приехал бы ровно
**Опоздание — заказ прячется от ранних слепков.** `created_at` не
подделывается — строка создана, когда заказ родился, — но в слепках дней
d…d+δ−1 её нет, а с d+δ она появляется в том состоянии, до которого заказ
дожил: отменённый на второй день и опоздавший на третий приедет в первом же
своём слепке как `cancelled` — «выгрузка догоняет жизнь». Задержка — таблица
весов из трёх строк: 0 на подавляющем весе, 1 и 2 — это и есть «D+1/D+2»
мастер-спеки. Меряется она в днях снятия слепка, а не отправки: иначе сдвиг
отправки удвоился бы, и обещанные D+1/D+2 стали бы D+2/D+3. Дальше таблица не
идёт: заказ с δ = 6 приехал бы ровно
в одном слепке, и обещание «пропущенный день ничего не ломает» на нём
перестало бы быть верным; при δ ≤ 2 у всякого заказа слепков не меньше пяти.
Легенда: заказ ушёл в ручную обработку и попал в выгрузку позже. Следствие для
@@ -263,7 +270,8 @@
**Пересечения.** Броски независимы, пересечения выходят арифметикой, приоритет
мастер-спеки работает по-настоящему. Исключений два, и оба названы выше: дубль
решается только у выживших покупок, и назначенное планом не портится.
решается только у выживших покупок, а назначенное планом не теряется и не
опаздывает — дельта и отмена ему разрешены, моста они не рвут.
Следствие для калибровки: брошенная доля и наблюдаемая в сверке — разные числа
(часть заказов забирают победители по приоритету, часть у класса C недоступна —
однопозиционных заказов больше половины). Отклонено: *один класс на заказ*
@@ -346,8 +354,10 @@
границе суток 7|8. Генератор играет два источника с разными темпами — поток
Метрики и ночную выгрузку магазина; разные темпы дают всё остальное.
`world-init` играет восемь дней одним прогоном `batch --day 0 --days 8`; со
сдвигом отправки уезжают **слепки дней 0…6**. Слепок дня 7 снят на границе
`world-init` играет восемь дней одним прогоном `batch --day 0 --days 8`;
слепки отправляет второй запуск — команда `snapshot` того же диапазона (два
источника — два запуска, раздел 2), и со сдвигом отправки уезжают **слепки
дней 0…6**. Слепок дня 7 снят на границе
суток и уедет первым же ходом мира. Особого режима у стартового мира нет:
правило отправки живёт в одном месте — в проигрывателе; генератору это решение
не стоит ничего.