Перейти к основному содержимому

Сторож недобора (Scope History Guard)

Если видишь scope history guard failed в last_error прогона, или run ушёл в partial без видимой причины — ты здесь.

Что охраняет

Резкое падение объёма сбора против собственной истории этого же scope. Сравнивает текущее itemsEmitted с медианой последних N успешных прогонов.

Не сравнивает с другими scope. Предыдущая версия (F-62) сравнивала FBY с FBS и давала ложные тревоги — склад FBY был вычерпан, и ноль заказов был правдой.

Где живёт

КомпонентПуть
Policy specspecs/products/ym.stats_orders.yaml (опциональное поле quality.scopeHistoryGuard)
Нормализация политикиmarketplace-collector-v3/spec_runtime/scope_history_guard.py
Вычисление решенияmarketplace-collector-v3/spec_runtime/scope_history_guard.py (scope_history_guard_reason)
Применение к прогонуspec_runtime/runner.py, collectors/base.py
Тестыmarketplace-collector-v3/tests/test_scope_history_guard.py

Параметры (для YM FBY)

ПараметрЗначениеОбоснование
historyRuns3Наименьшее нечётное окно для точной медианы
minHistoryItems10Выше проблемных 9: малый baseline не даёт тревогу
minCurrentPermille200Для случая 9/49 = 183,7‰ — срабатывает
minAvailableStock5Выше задокументированных остатков 4 и 2 после исчерпания
stockSourceym.stocksТолько YM stocks поддерживаются

Как отказывает

Сторож НЕ кидает исключение. Он переводит run в статус partial и пишет причину в last_error:

scope history guard failed: fby itemsEmitted=9 below 200/1000 of its own median=49 across 3 successful runs; availableStock=3214

Если нет трёх корректных receipt, нет свежего снимка остатков, baseline слишком мал, или остаток ниже порога — сторож молчит (None). «A false partial would be worse than no verdict.»

Когда НЕ срабатывает (и это правильно)

СлучайРезультат
Июль FBY: 0 заказов, остаток 4Молчит — остаток ниже minAvailableStock=5
Август FBY: 0 заказов, остаток 2Молчит — остаток ниже порога
FBS: обычные объёмыМолчит — guard только для FBY
Нет 3 успешных receiptМолчит — недостаточно истории
Новый ProductSpec digestМолчит — прогрев после изменения spec

Что делать при отказе

  1. Проверить last_error прогона:
    SELECT status, last_error, scope_evidence_json
    FROM gwptd_intake.collection_run
    WHERE endpoint_code = 'ym.stats_orders'
    ORDER BY created_at DESC LIMIT 1;
  2. Сравнить с предыдущими успешными прогонами ТОГО ЖЕ scope:
    SELECT collected_at, status, scope_evidence_json
    FROM gwptd_intake.collection_run
    WHERE endpoint_code = 'ym.stats_orders' AND status = 'success'
    ORDER BY collected_at DESC LIMIT 5;
  3. Проверить остатки:
    SELECT collected_date, SUM(available_count) AS stock
    FROM gwptd_intake.ym_stocks
    WHERE mp_id = 8
    GROUP BY collected_date ORDER BY collected_date DESC LIMIT 5;
  4. Если падение реальное — разбираться с API площадки.
  5. Если ложное — настраивать пороги. НЕ отключать guard.

Почему не сравниваем с соседом

Предыдущая версия сравнивала FBY с FBS. Ложная тревога хуже отсутствия тревоги: её быстро начинают игнорировать, и вместе с ней проигнорируют настоящую.

FBS и FBY — независимые схемы доставки с независимыми складами. Их объёмы не обязаны соотноситься. Склад FBY может быть вычерпан — и это правда, а не авария.

Границы

  • Сторож НЕ ловит логические ошибки в данных (перепутанный статус, неверная цена).
  • Сторож работает только для YM (ym.stocks — единственный stockSource).
  • Сторож НЕ проверяет другие endpoint'ы.
  • Сторож требует configured static scopesruntime_discovered не работает.