# Senior QA Review — Integrity Scanner ## Urteil: **PASS WITH CONCERNS** --- ### ✅ Bestanden (8/8) | Check | Ergebnis | Befehl/Evidenz | |---|---|---| | Module-Import | 11/11 ✅ | `python3 -c "import scanner.*"` — alle OK | | Tests | **266/266 grün** (2.95s) ✅ | `pytest tests/ -v` | | CLI | 14 Subcommands, `--help` funktioniert ✅ | `python3 -m scanner --help` | | Pipeline-Check | Crawler → Extractor → Baseline → Differ → Scoring ✅ | `python3 -c "from scanner.differ import compare_snapshots"` | | Echter Check (`bredelar.info`) | 🟢 Score 0, 46 Seiten geprüft ✅ | `python3 -m scanner --config bredelar.info/config.yaml check` | | Report-Generierung | JSON + Markdown ✅ | `bredelar.info/reports/20260614_171847/report.md` | | KI-Analyse | OpenRouter primary + localhost Fallback, hash-gegated ✅ | `_openrouter_chat()` graceful degradation | | Dependencies | requests 2.32, bs4 4.13, lxml 5.4, PyYAML 6.0, pytest 8.3 ✅ | `requirements.txt` erfüllt | --- ### 🔴 Blocker (0) Keine. --- ### 🟡 Major (2) — unverändert vom letzten Review **M1: `exclude_paths=["/"]` bei jamulix.de blockt ALLE Pfade** *Datei:* `jamulix.de/config.yaml` (Zeile 28-30) *Befehl:* `python3 -c "from scanner.crawler import should_crawl; print(should_crawl('https://jamulix.de/podcasts/episode-1', 'jamulix.de', [], ('/hackernews/', '/'), ('/podcasts/',)))"` → `False` *Reproduktion:* `exclude_paths=['/']` + `p.path.startswith('/')` → **jeder Pfad** wird blockiert. Baseline hat nur **1 Seite**. *Fix:* `"/"` aus `exclude_paths` entfernen ODER Logik ändern: exakte Pfad-Treffer prüfen (`p.path == "/"`), nicht `startswith`. **M2: Seed-URL umgeht `should_crawl`-Filter** *Datei:* `scanner/crawler.py` (Zeile 10-16) *Befehl:* `grep -n "should_crawl" scanner/crawler.py` → nur Zeile 264, 281 (Sitemap + Link-Extraktion), **nicht** Zeile 11-16 (Seed-Queue) *Reproduktion:* Bei jamulix.de wird `/podcasts/` direkt gecrawlt, obwohl `should_crawl()` es als `False` bewerten würde. *Fix:* Seed-URLs durch `should_crawl()` filtern. --- ### 🔵 Minor (4) — unverändert vom letzten Review | Issue | Datei | Risiko | |---|---|---| | **Mi1:** Test-Coverage für `__main__.py` nur 10% | `tests/` | Integrations-Logik kaum getestet | | **Mi2:** Keine Versions-Pins in `requirements.txt` | `requirements.txt` | Breaking Changes bei Updates | | **Mi3:** Kein File-Lock für parallele Scans | `scanner/__main__.py` | Cron-Job + manueller Aufruf kollidieren | | **Mi4:** `refresh_catalog()` ohne Lock | `scanner/ai_analyzer.py` | Race Condition bei parallelen Scans | --- ### 📊 Fehlende Tests (Coverage < 40%) | Modul | Coverage | Fehlend | |---|---|---| | `__main__.py` | **10%** | `cmd_scan`, `cmd_check`, `_worst_level`, Report-Pipeline | | `crawler.py` | **28%** | Live-Crawl, Sitemap-Parsing, Seed-URL-Logik | | `config.py` | **28%** | `resolve_paths()`, Config-Merge | | `report.py` | **37%** | Markdown-Rendering, JSON-Export | --- ### ⚠️ Produktionsrisiken 1. **Jamulix-Crawl:** Nur 1 Seite gecrawlt (M1 + M2). Bei `check` → alle anderen URLs als "fehlend" gemeldet. 2. **Parallele Scans:** Kein File-Lock → Cron-Job + manueller Aufruf können Snapshots korrupt machen. 3. **Dependency-Updates:** Keine Obergrenzen in `requirements.txt` → potentielles Breaking Change bei `requests` v3.x oder `beautifulsoup4` v5.x. 4. **OpenRouter-Rate-Limits:** Im Produktivbetrieb ohne KI-Cache → viele API-Calls. Circuit-Breaker hilft, aber bei 46 Seiten × 2000 Zeichen = ~92k Zeichen pro Scan. --- ### 🔧 Konkrete Fix-Aufträge an den Coder 1. **Fix M1:** `jamulix.de/config.yaml` → `"/"` aus `exclude_paths` entfernen. 2. **Fix M2:** `scanner/crawler.py` Zeile 10-16 → Seed-URLs durch `should_crawl()` filtern. 3. **Add Lock:** `scanner/__main__.py` → File-Lock vor `cmd_scan` (`fcntl.flock` oder `portalocker`). 4. **Add Tests:** `tests/test_main.py` → Integrationstests für `cmd_scan`, `cmd_check`, `_worst_level`. 5. **Pin Versions:** `requirements.txt` → Obergrenzen hinzufügen (z.B. `requests>=2.31,<3.0`). --- ### Fazit Das Projekt **funktioniert** und ist **produktionsreif für bredelar.info** (46 Seiten, alle Checks grün). Die Hauptprobleme sind: - Jamulix-Crawl nur 1 Seite (Konfigurations-Bug + Code-Bug) - Test-Coverage für `__main__.py` nur 10% **Empfehlung:** M1 + M2 fixen → dann **PASS**.