Зачем: собранная спека заказов стала базовой документацией сервиса, а жанр спеки-события ей мал: дата в имени врёт, целиком в контекст агента она не влезает, а трекер с резолюциями долговечным хранилищем не считается. Решение владельца — держать детальное устройство компонента связным набором живых документов (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>
74 lines
8.0 KiB
Markdown
74 lines
8.0 KiB
Markdown
# Классы расхождений и опоздание
|
|
|
|
Резолюция развилки [«Расхождения A–D и опоздания: механика, доли и что
|
|
обещано»](https://git.dementev.space/ddmitry/clickstream-data-platform/issues/72).
|
|
Классы и ориентиры долей — [мастер-спека,
|
|
раздел 4](../../specs/2026-07-30-stand-v2-realism.md); здесь — механика
|
|
каждого.
|
|
|
|
**Дельта суммы (C) — вычеркнутая позиция.** Товара не оказалось в наличии,
|
|
позицию сняли: у заказа на одну позицию меньше, чем в клиентских массивах, а
|
|
`items_total` меньше на её стоимость. Момента у неё нет — заказ приезжает
|
|
урезанным во всех своих слепках: первый слепок снимается на границе суток,
|
|
когда склад заказ уже собрал. Заказ из одной позиции дельты не получает —
|
|
пустых заказов не бывает. Позиция выбирается равновероятно: корреляция со
|
|
спросом на доле 1–2% статистически ненаблюдаема — менти платил бы за неё
|
|
таблицей чисел мира, а увидеть не мог бы ничем. Доводы за вычёркивание:
|
|
остаток — сотни рублей, он торчит в витрине сверки сам; расхождение
|
|
объясняется сравнением позиций — разбором вложенного JSON и `ARRAY JOIN`,
|
|
ровно тем навыком, ради которого позиции разбираются; история рассказывается
|
|
словами без легенды про генератор. Отклонено: *переоценка позиции* и *другое
|
|
количество* — дельта в десятки рублей, её надо захотеть заметить; *чистая
|
|
дельта без истории* — тупик, объяснить нечем; *врёт клиент, а не бэкенд* —
|
|
заказ у нас проекция той же корзины.
|
|
|
|
**Потеря события (B) — точечная.** Уходит строка `purchase`, просмотр
|
|
`/confirmation` остаётся: события уезжают разными запросами, потерять один и
|
|
сохранить другой — обычное дело. Единственный вариант, при котором потеря
|
|
видна со стороны трекера: до подтверждения дошли сто, покупок девяносто семь.
|
|
|
|
**Дубль события (D) — сюжетный.** Обновление страницы шлёт и просмотр, и
|
|
покупку. Довод не в связности легенды: точечный дубль ломал бы урок соседнего
|
|
класса — разрыв воронки, на котором держится потеря, сжался бы втрое; при
|
|
сюжетном разрыв снова равен доле потерь. `purchaseID`, суммы и позиции у дубля
|
|
один в один — посчитал наивно, удвоил выручку. Дубль случается только там, где
|
|
до следующего визита куки остаётся запас сверх таймаута: иначе сборка сессий у
|
|
менти разошлась бы с `VisitID` — сломался бы эталон, ради которого `VisitID`
|
|
в потоке лежит. Задержка дубля — секунды-минуты, короче таймаута визита,
|
|
поэтому `VisitID` тот же. Полночь режет дубль парой — просмотр вместе с
|
|
покупкой, по тому же правилу, что у подтверждения с торговым хвостом.
|
|
Отклонено: *обе точечные* — дубль затирает урок потери; *обе сюжетные* —
|
|
потеря перестаёт быть видна со стороны трекера.
|
|
|
|
**Гарантия моста сильнее порчи.** Назначенные планом покупки не теряются, и
|
|
назначенные планом заказы не опаздывают: `dds.identity_map` строится из моста
|
|
«`purchase` ↔ заказ», и выброшенное событие — как и заказ, не попавший ни в
|
|
один снятый слепок, — уносит куку из карты. Это был бы отказ лабы склейки, а
|
|
не расхождение в данных; менти различить не может.
|
|
|
|
**Опоздание — заказ прячется от ранних слепков.** `created_at` не
|
|
подделывается — строка создана, когда заказ родился, — но в слепках дней
|
|
d…d+δ−1 её нет, а с d+δ она появляется в том состоянии, до которого заказ
|
|
дожил: отменённый на второй день и опоздавший на третий приедет в первом же
|
|
своём слепке как `cancelled` — «выгрузка догоняет жизнь». Задержка — таблица
|
|
весов из трёх строк: 0 на подавляющем весе, 1 и 2 — это и есть «D+1/D+2»
|
|
мастер-спеки. Меряется она в днях снятия слепка, а не отправки: иначе сдвиг
|
|
отправки удвоился бы, и обещанные D+1/D+2 стали бы D+2/D+3. Дальше таблица не
|
|
идёт: заказ с δ = 6 приехал бы ровно в одном слепке, и обещание «пропущенный
|
|
день ничего не ломает» на нём перестало бы быть верным; при δ ≤ 2 у всякого
|
|
заказа слепков не меньше пяти. Легенда: заказ ушёл в ручную обработку и попал
|
|
в выгрузку позже. Отклонено: *сдвиг `created_at`* — подделка аудита источника:
|
|
день создания строки разошёлся бы с днём покупки, чьё равенство держит
|
|
синхронная модель ([откуда берётся заказ](projection.md)), заказ уехал бы в
|
|
чужую партицию, и «выручка дня D» перестала бы отвечать покупкам дня D —
|
|
сломалась бы та самая сверка, ради которой всё строится.
|
|
|
|
**Пересечения.** Броски независимы, пересечения выходят арифметикой, приоритет
|
|
мастер-спеки работает по-настоящему. Исключений два, и оба названы выше: дубль
|
|
решается только у выживших покупок, а назначенное планом не теряется и не
|
|
опаздывает — дельта и отмена ему разрешены, моста они не рвут.
|
|
Следствие для калибровки: брошенная доля и наблюдаемая в сверке — разные числа
|
|
(часть заказов забирают победители по приоритету, часть у класса C недоступна —
|
|
однопозиционных заказов больше половины). Отклонено: *один класс на заказ* —
|
|
приоритет в SQL стал бы мёртвой веткой, которую менти читает как живую.
|