Сторож недобора (Scope History Guard)
Если видишь
scope history guard failedвlast_errorпрогона, или run ушёл вpartialбез видимой причины — ты здесь.
Что охраняет
Резкое падение объёма сбора против собственной истории этого же scope.
Сравнивает текущее itemsEmitted с медианой последних N успешных прогонов.
Не сравнивает с другими scope. Предыдущая версия (F-62) сравнивала FBY с FBS и давала ложные тревоги — склад FBY был вычерпан, и ноль заказов был правдой.
Где живёт
| Компонент | Путь |
|---|---|
| Policy spec | specs/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)
| Параметр | Значение | Обоснование |
|---|---|---|
historyRuns | 3 | Наименьшее нечётное окно для точной медианы |
minHistoryItems | 10 | Выше проблемных 9: малый baseline не даёт тревогу |
minCurrentPermille | 200 | Для случая 9/49 = 183,7‰ — срабатывает |
minAvailableStock | 5 | Выше задокументированных остатков 4 и 2 после исчерпания |
stockSource | ym.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 |
Что делать при отказе
- Проверить
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; - Сравнить с предыдущими успешными прогонами ТОГО ЖЕ 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; - Проверить остатки:
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; - Если падение реальное — разбираться с API площадки.
- Если ложное — настраивать пороги. НЕ отключать guard.
Почему не сравниваем с соседом
Предыдущая версия сравнивала FBY с FBS. Ложная тревога хуже отсутствия тревоги: её быстро начинают игнорировать, и вместе с ней проигнорируют настоящую.
FBS и FBY — независимые схемы доставки с независимыми складами. Их объёмы не обязаны соотноситься. Склад FBY может быть вычерпан — и это правда, а не авария.
Границы
- Сторож НЕ ловит логические ошибки в данных (перепутанный статус, неверная цена).
- Сторож работает только для YM (ym.stocks — единственный
stockSource). - Сторож НЕ проверяет другие endpoint'ы.
- Сторож требует configured static scopes —
runtime_discoveredне работает.