Closing: already implemented in main. Batch.tsx and Analytics.tsx include skeleton loaders (animate-pulse) and error state displays.
Closing: already implemented in main. types.py declares Patent.patent_id as str.
Closing: already implemented in main. analyzer.py downloads PDFs before reading from disk (line 139: 'PDF not on disk; downloading from cached link').
Closing: already implemented in main. config.py reads SERP_CACHE_TTL_HOURS env var with default 24. Documented in .env.example.
Closing: already implemented in main. config.py reads MODEL env var with default anthropic/claude-3.5-sonnet. llm.py uses config.model. Documented in .env.example.
Closing: already implemented in main. All print() calls in analyzer.py, serp_api.py, and llm.py have been replaced with logging.getLogger(__name__). config.py sets up logging with…
Closing: already implemented in main. tests/test_auth.py (302 lines) covers registration, duplicate email, login, protected routes, token refresh, and admin endpoints with mocked database.
Closing: already implemented in main. api.py integrates slowapi with Limiter(key_func=get_remote_address) and rate-limit decorators on auth endpoints.
Closing: already implemented in main. database.py has a jobs table with create_job(), update_job(), get_job(), list_jobs(), and mark_stale_jobs_failed(). Jobs persist in PostgreSQL…
Closing: already implemented in main. auth.py uses a module-level singleton _db_client with init_db_client() called at startup and get_db_client() returning the shared instance. `DatabaseCl…