Files
breakpilot-core/control-pipeline/docs/incremental-dedup.md
T
Benjamin Admin 9783657da3
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 43s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 37s
feat(control-pipeline): incremental dedup + ENISA CRA ingestion
BatchDedup since-Parameter (services/batch_dedup_runner.py + api):
- Neuer 'since: datetime' Param scoped Phase 1 + Phase 2 SQL auf created_at >= since.
- Phase 2 checkpoint wird beim scoped Lauf geloescht (verhindert Skip neuer Atomics
  deren control_id alphabetisch unter dem stale last_id liegt).
- 6-13x schneller fuer nachgeschobene Dokumente (19k statt 172k Atomics).
- Doku: control-pipeline/docs/incremental-dedup.md.

Neue Scripts:
- gpre1_object_groups_incremental.py: Append neuer Objects an object_groups via
  bge-m3 nearest-neighbor (threshold default 0.85, empfehlbar 0.78 fuer breiteres
  Synonym-Matching). Pure INSERT/UPDATE, kein DELETE.
- gpre2_master_controls_incremental.py: Non-destructive Master-Controls-Update.
  Existing MCs unangetastet (UUIDs + master_control_id bleiben), nur neue Members
  appended + neue MCs fuer Object-Groups die jetzt min-phases erreichen.
- ingest_enisa_cra.py: Ingestion der 8 CRA-relevanten ENISA-Dokumente
  (Standards Mapping, EUCC-Implementation, NIS2 TIG, SRP FAQ, EUCC Eval Methodology,
  CVD Policies, Threat Landscape 2025). chunk_strategy=legal,
  requirement_strength=guidance|consultation_draft|evidentiary.

Quelldaten: legal-sources/enisa/enisa_cra_single_reporting_platform_faq.html
(PDFs sind .gitignore-gefiltert).

Ergebnis dieser Pipeline-Iteration:
- 1.296 neue CRA-Controls + 19.652 atomare Children
- +362 neue Master-Controls, 10.017 existing erweitert
- Total: 13.950 MCs, 620 CRA-MCs (vorher 566), 1.304 CRA-Atomics (vorher 841)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:21:46 +02:00

3.7 KiB

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:

{
  "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

# 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": "<ISO-Datetime kurz vor Pass-0b-Start>"
}'

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.pyrun() + _load_merge_groups() + _run_cross_group_pass() SQL-Queries
  • api/control_generator_routes.pyBatchDedupRequest.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