# 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**.
