DNB cover-validation: drop synchronous probe, add universal <img onerror> fallback (#32)
Why
Live probe of portal.dnb.de shows the cover endpoint resets the TLS connection (instead of returning HTTP 404) for ISBNs without a cover. The pre-fix code ran a 10-second synchronous `requests.get` per ISBN, racking up ~60-100s of latency per search and ERROR-level log spam from `SSLError(SSLEOFError)`.
What
- Default cover-validation strategy is now `skip` — return the URL unconditionally, let the modal's `` swap to the generic SVG fallback when the upstream URL is broken.
- Opt-in modes via `CWA_DNB_COVER_VALIDATION={skip|head|get}`, both with:
- 4-second probe timeout (was 10s), env-overridable via `CWA_DNB_COVER_PROBE_TIMEOUT`.
- `functools.lru_cache(maxsize=2048)` so repeat searches for the same ISBN are free.
- Explicit catch of `SSLError` / `ConnectionError` / `Timeout` → DEBUG. The TLS reset is the expected miss-signal, not an ERROR.
- Universal `` fallback in `cps/templates/book_edit.html` — broken cover URLs from any provider degrade gracefully to the generic cover SVG instead of a browser-default broken-image icon.
Verified live
```
DNB search('Animal Farm george orwell','','en')
pre-fix: 60-100s + many SSLError ERROR records
post-fix: 0.81s + 0 ERROR records (~100× speedup)
```
Tests
- 14 new pytest smoke cases — mode resolution, skip-mode (no HTTP), head-mode happy-path, SSL/Connection/Timeout swallowing, LRU caching.
- 15 new autopilot guards pinning the new contract.
- Full autopilot suite (18 files) green.
Upgrade
```bash
docker compose pull && docker compose up -d
```
No DB migration. No thumbnail refresh. Optional env knob `CWA_DNB_COVER_VALIDATION=head` if you want the legacy HEAD-validation behaviour back.
Full diff: v4.0.10...v4.0.11