- admin/routes.py: add LIMIT 500 to scenarios() — was unbounded, could return
arbitrarily large result sets and exhaust memory
- analytics.py: wrap asyncio.to_thread(DuckDB) in asyncio.wait_for with
_QUERY_TIMEOUT_SECONDS=30 so a slow scan cannot permanently starve the
asyncio thread pool
- core.py: replace resend.default_http_client with RequestsClient(timeout=10)
so all Resend API calls are capped at 10 s (default was 30 s)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>