Review-bot PR #520 SHA dc1cd94 нашёл medium discrepancy: `get_all` НЕ raises
на DB unreachable — оно catches Exception и возвращает _DEFAULTS (4 synthetic
rows, scrape_kn + objective_sync enabled). Значит `except` блок в
build_beat_schedule и `rows_seen == 0` branch — dead code, никогда не fires.
Primary fix всё равно работает (operator-disable создаёт реальные rows c
enabled=false → loop их skip'ает → schedule={} → respect). Но контракт в
docstring ("strict, raises on failure") был fiction.
Cleanup (Option A из review):
- Убрана try/except в build_beat_schedule (dead — get_all не raises)
- Убрана rows_seen == 0 branch (dead — get_all всегда returns >=4 rows из _DEFAULTS)
- Сигнатура `_build_beat_schedule_from_db` обратно `-> dict` (не tuple)
- Docstring переписан под реальное поведение: schedule пуст ТОЛЬКО при
operator-initiated disable; DB-unreachable case замаскирован под _DEFAULTS
(синонимично env-fallback по итогу: scrape_kn + objective_sync с дефолтным cron)
Net behavior identical для всех 4 сценариев (operator-disable, normal-config,
DB-unreachable, fresh-install) — но код теперь честный.
UPDATE job_settings SET enabled=false для всех cron-able jobs приводил к
пустому schedule из DB — caller (build_beat_schedule) интерпретировал
'not schedule' как 'DB error' и срывался на env-based fallback, который
повторно добавлял scrape_kn + objective_sync из settings.scrape_kn_cron /
objective_sync_cron. Disable через DB был **бесполезен**.
Incident 2026-05-24: WAF hard-ban на VPS IP от DOM.РФ → попытка отключить
scrape_kn + objective_sync через UPDATE job_settings — fallback вернул их
обратно. Беспорядок виден в beat logs: 'beat_schedule: строим fallback из
env vars' при наличии rows в БД.
Fix:
- `_build_beat_schedule_from_db` теперь использует `get_all` (strict, raises)
вместо `get_all_safe` (silently возвращает _DEFAULTS)
- Возвращает (schedule, rows_seen) tuple
- Caller fallback'ает ТОЛЬКО при `not db_reachable` (exception) ИЛИ
`rows_seen == 0` (fresh install, безопасный fallback для setup)
- При rows_seen > 0 + schedule={} — RESPECT user intent (все disabled)
- Hardcoded entries (OSM POI/noise, nspd cleanup, refresh_analytics)
добавляются ВСЕГДА сверху — они не управляются job_settings