# Incremental BatchDedup für nachgeschobene Dokumente Eingefuehrt am 2026-05-18. Pattern fuer alle zukuenftigen Einzeldokument-Ingestionen. ## Problem Der Default-BatchDedup-Runner lief gegen ALLE `pass0b` Atomics ohne Filter (WHERE decomposition_method = 'pass0b' AND release_state NOT IN ('deprecated','duplicate')). Das sind bei uns ~172k Controls. Pace ~5k/h → 25-40h Laufzeit. Bei jedem hinzugefuegten Dokument der gleiche volle Lauf — auch wenn das neue Dokument nur 1-2k Atomics erzeugt. Zusaetzliches Risiko: Phase 1 schreibt master_controls erst am Ende. Ein Container-Crash mitten im Lauf (z.B. via Qdrant-Timeout) verwirft 100% des In-Memory-Fortschritts. ## Loesung — `since` Parameter `POST /v1/canonical/generate/batch-dedup` akzeptiert jetzt: ```json { "dry_run": false, "since": "2026-05-18T02:53:00+00:00" } ``` Effekt: - Phase 1 (intra-group dedup) laedt nur Controls mit `created_at >= since` - Phase 2 (cross-group dedup) filtert ebenfalls auf `created_at >= since` - Phase 2 Checkpoint wird vor Lauf-Start geloescht (sonst skippt stale `last_control_id` neu erzeugte Atomics deren control_id alphabetisch davor liegt) Phase 2 sucht weiter im **vollen** Qdrant-Index `atomic_controls_dedup`, findet also Matches zu alten Master Controls und verlinkt korrekt. ## Wann verwenden | Szenario | Empfehlung | |---|---| | Einzelnes neues Dokument ingestiert + Pass 0a + Pass 0b durchgelaufen | `since` setzen auf Zeitpunkt vor Pass 0b | | Mehrere kleine Updates seit letztem Full-Dedup | `since` setzen auf Zeitpunkt nach letztem Full-Dedup | | Initial-Setup oder Pipeline-Major-Update | KEIN `since` — full run | | Verdacht auf Drift / Quality-Regression | KEIN `since` — full run | ## Workflow nach Einzeldokument-Ingestion ```bash # 1. Pass 0a auf neue Controls (Obligations extrahieren) curl -X POST .../v1/canonical/generate/run-pass0a -d '{...}' # 2. Pass 0b Decomposition Submit (Atomics erzeugen) curl -X POST .../v1/canonical/generate/submit-pass0b -d '{...}' # 3. Wenn Anthropic Batch durch: process-batch curl -X POST .../v1/canonical/generate/process-batch -d '{ "batch_id": "msgbatch_...", "pass_type": "0b" }' # 4. Inkrementell deduppen (NEU, statt 25h full run) curl -X POST .../v1/canonical/generate/batch-dedup -d '{ "dry_run": false, "since": "" }' ``` ## Pace-Beobachtung (CRA-Lauf 2026-05-18) - Total neue Atomics: 19.423 - Phase 1 multi-groups: 568 (Rest 18.101 sind Singletons → direkt Master) - Phase 2 Cross-Group: ~3-4h erwartet - Vergleich: Full-Run waere 25-40h gewesen, scoped 6-13x schneller. ## Implementation-Details (fuer Wartung) Geaenderte Dateien: - `services/batch_dedup_runner.py` — `run()` + `_load_merge_groups()` + `_run_cross_group_pass()` SQL-Queries - `api/control_generator_routes.py` — `BatchDedupRequest.since` Feld + Handler reicht durch Backwards-kompatibel: ohne `since` aequivalent zum alten Verhalten. ## Bekannte Limits 1. **Phase 2 Checkpoint wird beim scoped Lauf geloescht.** Wenn waehrend eines `since`-Laufs ein voller Run dazwischen geschoben werden soll (sollte nicht passieren), muss neu starten. 2. **Phase 1 commit-Granularitaet nicht angefasst.** Bei Crash mitten in Phase 1 ohne `since` bleibt der Verlust gleich. Aber: scoped Phase 1 ist so kurz (Minuten), dass das praktisch egal ist. 3. **Singleton-Atomics werden direkt Master ohne Cross-Check.** Wenn ein neues Singleton-Atomic semantisch identisch zu einem alten Master ist, faengt das nur Phase 2 (via Qdrant). Funktioniert solange Phase 2 nicht uebersprungen wird (dry_run=false ist Pflicht). ## Memory-Eintrag Siehe `~/.claude/projects/-Users-benjaminadmin-Projekte-breakpilot-core/memory/feedback_incremental_dedup.md`