docs(orders): устройство заказов переехало в живой набор architecture/orders

Зачем: собранная спека заказов стала базовой документацией сервиса, а
жанр спеки-события ей мал: дата в имени врёт, целиком в контекст агента
она не влезает, а трекер с резолюциями долговечным хранилищем не
считается. Решение владельца — держать детальное устройство компонента
связным набором живых документов (ADR 0011) и совместить переезд с
проходом на вычитание (#84).

Что: docs/architecture/orders/ — индекс README и файлы по частям
устройства: проекция, слепок и доставка, судьба, классы расхождений,
опись, мост к склейке, стартовый мир, правила кода; спека приёма
переехала в ingestion.md без содержательных правок. Резы вычитания по
итогам двух слепых линий: тела разделов о проводе и приёме сведены к
указателям на мастер-спеку, исследование формата и ADR (порядок строк
слепка — единственное правило, оставшееся на месте); замеры канонического
зерна и повторы-пояснения срезаны; списки отклонённых вариантов сохранены
как долговечная запись. Датированные файлы удалены, ссылки из мастер-спеки,
спеки генератора, ADR 0008/0010, исследования формата и storage.md
перенацелены; раздел «Структура» AGENTS.md дополнен правилом подпапки.

Проверка: grep по репозиторию не находит ссылок на удалённые файлы;
все относительные ссылки внутри набора разрешаются в существующие файлы;
впереди холодная сверка «ни одно решение не потеряно» и приёмка #88.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 23:50:13 +03:00
co-authored by Claude Fable 5
parent b9f570fc40
commit 0d9a83d6af
19 changed files with 498 additions and 573 deletions
+66
View File
@@ -0,0 +1,66 @@
# Судьба заказа
Резолюция развилки [«Расхождения A–D и опоздания: механика, доли и что
обещано»](https://git.dementev.space/ddmitry/clickstream-data-platform/issues/72).
**Рамка.** Расхождение — не грязь и не шум, а часть мира: судьба заказа,
решённая при его рождении и уложенная в окно K целиком.
**Два подпотока дня.** Заказная сторона — судьба заказа: исход, моменты,
дельта, опоздание. Событийная сторона — порча событийного потока: потеря и
дубль. Довод за разделение — различимость по описи: правка заказной механики
не двигает хеши событий, правка событийной не двигает байты слепка, и по
покрасневшим хешам видно, какую сторону трогали. Прежние имена `DISCREPANCIES`
и `LATECOMERS` решения не переживают — они названы по классам витрины, а
компонент называет часть мира; новые стороны занимают те же позиции, имена —
при реализации. Отклонено: *один компонент на всю судьбу* — правка событийной
механики молча меняла бы байты слепка; *компонент на класс* — пять имён под
ручки калибровки, которые крутятся разом.
**Правило формы, без которого разделение не работает: броски заказной стороны
делаются на полную длину дня, а не на отобранных заказах.** Иначе длина броска
становится функцией доли, и правка одной доли перебрасывает весь подпоток
после себя. Изоляцию даёт форма броска, а не число подпотоков.
**Путь по статусам.** Три исхода, все внутри окна: **оплачен**; **оплачен и
отменён**; **не оплачен и отменён**. Инвариант на выходе из окна: заказ либо
`paid`, либо `cancelled`; `created` — только промежуточное состояние. За окном
будущего у заказа нет, а заказ, навсегда застрявший в `created`, — модельная
небрежность, которой в выгрузке живого магазина соответствия нет. Поэтому
нового значения `mismatch_class` не нужно: `cancelled` покрывает обе дороги
отмены (шестое значение занято `awaiting_order`).
**Форма броска.**
1. **Исход** — таблица долей из трёх строк; доля неоплаченных пишется явной
строкой, а не оставляется читателю складывать хвост в уме.
2. **Моменты** — таблица целых весов «сколько часов от рождения — с каким
весом», строки 0…143, плюс равномерная секунда внутри часа — чтобы разности
времён аудита не давали точных равенств (урок правки #50).
3. Моментов бросается **всегда два, на полную длину дня**; у одномоментных
исходов второй выбрасывается. Где их два по существу, ранний считается
оплатой — порядок выходит сортировкой, условной точки отсчёта не нужно.
143 часа — самый узкий край окна: у заказа, рождённого в конце суток, до
последнего его слепка 144 часа. Таблица кончается там, где кончается окно у
самого невезучего: вылезти нечему, сторожа не нужно. Цена — заказ, рождённый в
начале суток, не использует почти сутки своего окна; в хвосте таблицы веса
мизерные, в данных это не видно. Форма из #71 — «вес за краем окна означает
„не оплачен никогда“» — этим отменена: такой заказ теперь отменяется, а край
окна не выражается числом часов — иначе доля неоплаченных стала бы функцией
часа покупки, и менти нашёл бы этот наклон первым же разрезом.
**Доли.** Ориентир мастер-спеки ~5% читается как доля отменённых вообще; как
она делится между двумя дорогами — строки таблицы исходов. Точные числа —
калибровка этапа 7; проверок вида «отмен от 4 до 6 процентов» не заводим.
**Что из двух дорог видно.** В `dds.order` дороги неразличимы — там последняя
версия; различает их история версий: сырьё STG и физические версии
`ods.order_snapshot` до фоновых слияний, а в витринах — выручка дня, которая
сначала выросла, потом убыла. Полное различение не обещано: слепок — состояние
на границе суток, и оплата с отменой в один день в сырье неразличимы; то же у
сильно опоздавших, приехавших уже терминальными. Точную форму этого урока
решает этап 4. Отклонено: *отмена только после оплаты* — неоплаченному некуда
деться, кроме как остаться брошенным; *мгновенная отмена при рождении*
«дыхание» окна на отменах исчезает; *отмен нет вовсе* — страховочный срез 1,
он в резерве.