# Control Generator Pipeline

Automatische Generierung von Canonical Controls aus dem gesamten RAG-Korpus (~105.000 Chunks aus Gesetzen, Verordnungen und Standards).

**Backend:** `backend-compliance/compliance/services/control_generator.py`
**Routes:** `backend-compliance/compliance/api/control_generator_routes.py`
**API-Prefix:** `/api/compliance/v1/canonical/generate`

---

## Pipeline-Uebersicht

Die Pipeline durchlaeuft 7 Stufen, um aus RAG-Chunks eigenstaendige Security/Compliance Controls zu erzeugen:

```mermaid
flowchart TD
    A[1. RAG Scan] -->|Alle Chunks laden| B[2. License Classify]
    B -->|Rule 1/2| C[3a. Structure Batch]
    B -->|Rule 3| D[3b. Reform Batch]
    C --> E[4. Harmonize]
    D --> E
    E -->|Duplikat| F[Als Duplikat markieren]
    E -->|Neu| G[5. Anchor Search]
    G --> H[6. Store Control]
    H --> I[7. Mark Processed]
```

| Stufe | Name | Beschreibung |
|-------|------|-------------|
| 1 | **RAG Scan** | Laedt unverarbeitete Chunks aus Qdrant (Scroll-API), filtert per SHA-256-Hash |
| 2 | **License Classify** | Bestimmt die Lizenzregel (Rule 1/2/3) anhand `regulation_code` |
| 3a | **Structure (Batch)** | Rule 1+2: Strukturiert Originaltext als Control (Anthropic API) |
| 3b | **Reform (Batch)** | Rule 3: Vollstaendige Reformulierung ohne Originaltext (Anthropic API) |
| 4 | **Harmonize** | Embedding-basierte Duplikaterkennung (bge-m3, Cosine > 0.85) |
| 5 | **Anchor Search** | Findet Open-Source-Referenzen (OWASP, NIST, ENISA) |
| 6 | **Store** | Persistiert Control in `canonical_controls` mit Metadaten |
| 7 | **Mark Processed** | Markiert jeden Chunk als verarbeitet (auch bei Skip/Error/Duplikat) |

---

## Pipeline-Versionen

Die Pipeline hat zwei Versionen. Die Version wird als `pipeline_version` auf `canonical_controls` und `canonical_processed_chunks` gespeichert.

### v1 (Original)

| Eigenschaft | Wert |
|-------------|------|
| **Vorfilter** | Lokales LLM (llama3.2 3B) entscheidet ob Chunk relevant |
| **Anthropic-Prompt** | Alter Prompt ohne null-Skip |
| **Annexe/Anhaenge** | Kein Schutz — wurden haeufig faelschlich als irrelevant uebersprungen |
| **`pipeline_version`** | `1` |

### v2 (Aktuell)

| Eigenschaft | Wert |
|-------------|------|
| **Vorfilter** | Optional (`skip_prefilter`). Wenn aktiviert, entscheidet Anthropic API selbst |
| **Anthropic-Prompt** | Neuer Prompt mit **null-Skip**: API gibt `null` fuer Chunks ohne Anforderung zurueck |
| **Annexe/Anhaenge** | Explizit geschuetzt — Prompt-Anweisung: "Anhaenge/Annexe enthalten oft KONKRETE technische Anforderungen — diese MUESSEN als Control erfasst werden!" |
| **`pipeline_version`** | `2` |

#### Wesentliche Aenderungen v1 → v2

1. **Relevanz-Entscheidung an Anthropic delegiert** — Das lokale LLM (Vorfilter) ist optional. Die Anthropic API entscheidet selbst, welche Chunks Controls enthalten, indem sie `null` fuer irrelevante Chunks zurueckgibt.
2. **null-Skip im JSON-Array** — Das Ergebnis-Array enthaelt `null`-Eintraege fuer Chunks ohne umsetzbare Anforderung. Kein separater Vorfilter-Schritt noetig.
3. **Annexe/Anhaenge geschuetzt** — Explizite Prompt-Anweisung verhindert, dass technische Anforderungen in Anhaengen uebersprungen werden.

#### Datenbank-Feld

```sql
-- Migration 062
ALTER TABLE canonical_controls
    ADD COLUMN pipeline_version smallint NOT NULL DEFAULT 1;

ALTER TABLE canonical_processed_chunks
    ADD COLUMN pipeline_version smallint NOT NULL DEFAULT 1;
```

Neue Controls erhalten automatisch `pipeline_version = 2`. Bestehende (v1) behalten `1`, damit sie spaeter identifiziert und ggf. reprocessiert werden koennen.

---

## Konfiguration

### Request-Parameter (`GenerateRequest`)

| Parameter | Typ | Default | Beschreibung |
|-----------|-----|---------|-------------|
| `collections` | `List[str]` | Alle 5 Collections | Qdrant-Collections zum Durchsuchen |
| `domain` | `str` | — | Filter auf eine Domain (z.B. `AUTH`, `NET`) |
| `regulation_filter` | `List[str]` | — | Prefix-Matching auf `regulation_code` (z.B. `["eu_2023_1230", "owasp_"]`) |
| `skip_prefilter` | `bool` | `false` | Ueberspringt lokalen LLM-Vorfilter, sendet alle Chunks an die Anthropic API |
| `batch_size` | `int` | `5` | Chunks pro Anthropic-API-Call |
| `max_controls` | `int` | `50` | Maximale Anzahl Controls pro Job (0 = unbegrenzt) |
| `max_chunks` | `int` | `1000` | Maximale Chunks pro Job (0 = unbegrenzt, respektiert Dokumentgrenzen) |
| `skip_web_search` | `bool` | `false` | Ueberspringt Web-Suche in der Anchor-Findung (Stufe 5) |
| `dry_run` | `bool` | `false` | Trockenlauf ohne DB-Schreibzugriffe (synchron, mit Controls im Response) |

!!! info "`regulation_filter` — Prefix-Matching"
    Der Filter vergleicht den `regulation_code` jedes Chunks per Prefix.
    Beispiel: `["eu_2023_1230"]` erfasst nur Chunks aus der Maschinenverordnung.
    `["owasp_"]` erfasst alle OWASP-Dokumente (OWASP ASVS, OWASP SAMM, etc.).
    Gross-/Kleinschreibung wird ignoriert.

### Umgebungsvariablen

| Variable | Default | Beschreibung |
|----------|---------|-------------|
| `ANTHROPIC_API_KEY` | — | API-Key fuer Anthropic Claude (Pflicht) |
| `CONTROL_GEN_ANTHROPIC_MODEL` | `claude-sonnet-4-6` | Anthropic-Modell fuer Strukturierung/Reformulierung |
| `OLLAMA_URL` | `http://host.docker.internal:11434` | Lokaler Ollama-Server (Vorfilter + QA) |
| `CONTROL_GEN_OLLAMA_MODEL` | `qwen3.5:35b-a3b` | Lokales LLM-Modell fuer Vorfilter und QA-Arbitrierung |
| `CONTROL_GEN_LLM_TIMEOUT` | `180` | Timeout in Sekunden pro Anthropic-API-Call |

### Pipeline-interne Konstanten

| Konstante | Wert | Beschreibung |
|-----------|------|-------------|
| `PIPELINE_VERSION` | `2` | Aktuelle Pipeline-Version |
| `HARMONIZATION_THRESHOLD` | `0.85` | Cosine-Similarity-Schwelle fuer Duplikaterkennung |
| `max_tokens` | `8192` | Maximale Token-Laenge der LLM-Antwort |

---

## API Endpoints

Alle Endpoints unter `/api/compliance/v1/canonical/`.

### Uebersicht

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| `POST` | `/generate` | Generierungs-Job starten (laeuft im Hintergrund) |
| `GET` | `/generate/status/{job_id}` | Status eines laufenden Jobs abfragen |
| `GET` | `/generate/jobs` | Alle Jobs auflisten (paginiert) |
| `GET` | `/generate/processed-stats` | Verarbeitungsstatistik pro Collection |
| `GET` | `/generate/review-queue` | Controls zur manuellen Pruefung |
| `POST` | `/generate/review/{control_id}` | Review eines einzelnen Controls abschliessen |
| `POST` | `/generate/bulk-review` | Bulk-Review nach `release_state` |
| `POST` | `/generate/qa-reclassify` | QA-Reklassifizierung bestehender Controls |
| `GET` | `/blocked-sources` | Gesperrte Quellen (Rule 3) auflisten |
| `POST` | `/blocked-sources/cleanup` | Cleanup-Workflow fuer gesperrte Quellen starten |

---

### POST `/v1/canonical/generate` — Job starten

Startet einen Generierungs-Job im Hintergrund. Gibt sofort eine `job_id` zurueck.

**Request:**

```json
{
  "collections": ["bp_compliance_gesetze"],
  "regulation_filter": ["eu_2023_1230"],
  "skip_prefilter": false,
  "batch_size": 5,
  "max_chunks": 500,
  "max_controls": 0,
  "skip_web_search": false,
  "dry_run": false
}
```

**Response (200):**

```json
{
  "job_id": "a1b2c3d4-...",
  "status": "running",
  "message": "Generation started in background. Poll /generate/status/{job_id} for progress."
}
```

**Beispiel:**

```bash
# Alle Chunks der Maschinenverordnung verarbeiten
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{
    "collections": ["bp_compliance_ce"],
    "regulation_filter": ["eu_2023_1230"],
    "max_chunks": 200,
    "batch_size": 5
  }'
```

```bash
# Dry Run: Keine DB-Aenderungen, Controls im Response
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{
    "collections": ["bp_compliance_gesetze"],
    "max_chunks": 10,
    "dry_run": true
  }'
```

```bash
# Ohne Vorfilter: Alle Chunks direkt an Anthropic API
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{
    "collections": ["bp_compliance_gesetze"],
    "regulation_filter": ["bdsg"],
    "skip_prefilter": true,
    "max_chunks": 100
  }'
```

!!! warning "Kosten beachten"
    Ohne `regulation_filter` und mit `max_chunks: 0` werden **alle** ~105.000 Chunks verarbeitet.
    Das verursacht erhebliche Anthropic-API-Kosten (~$700).

---

### GET `/v1/canonical/generate/status/{job_id}` — Job-Status

Gibt den vollstaendigen Status eines Jobs zurueck inkl. Metriken und Fehler.

**Beispiel:**

```bash
curl https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/status/a1b2c3d4-... \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
```

**Response:**

```json
{
  "id": "a1b2c3d4-...",
  "status": "completed",
  "total_chunks_scanned": 500,
  "controls_generated": 48,
  "controls_verified": 45,
  "controls_needs_review": 3,
  "controls_too_close": 0,
  "controls_duplicates_found": 12,
  "controls_qa_fixed": 5,
  "config": { "..." },
  "started_at": "2026-03-17T10:00:00+00:00",
  "completed_at": "2026-03-17T10:15:32+00:00"
}
```

---

### GET `/v1/canonical/generate/jobs` — Alle Jobs

Paginierte Liste aller Generierungs-Jobs.

**Query-Parameter:**

| Parameter | Default | Beschreibung |
|-----------|---------|-------------|
| `limit` | `20` | Anzahl Jobs (1-100) |
| `offset` | `0` | Offset fuer Paginierung |

**Beispiel:**

```bash
curl "https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/jobs?limit=5" \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
```

---

### GET `/v1/canonical/generate/review-queue` — Review-Queue

Listet Controls auf, die eine manuelle Pruefung benoetigen.

**Query-Parameter:**

| Parameter | Default | Beschreibung |
|-----------|---------|-------------|
| `release_state` | `needs_review` | Filter: `needs_review`, `too_close`, `duplicate` |
| `limit` | `50` | Anzahl (1-200) |

**Beispiel:**

```bash
curl "https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/review-queue?release_state=needs_review&limit=10" \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
```

---

### POST `/v1/canonical/generate/review/{control_id}` — Review abschliessen

Schliesst die manuelle Pruefung eines Controls ab.

**Request:**

```json
{
  "action": "approve",
  "release_state": "draft",
  "notes": "Inhaltlich korrekt, Severity passt."
}
```

**Moegliche `action`-Werte:**

| Action | Neuer State | Beschreibung |
|--------|-------------|-------------|
| `approve` | `draft` (oder per `release_state` ueberschreiben) | Control freigeben |
| `reject` | `deprecated` | Control verwerfen |
| `needs_rework` | `needs_review` | Zurueck in die Queue |

**Beispiel:**

```bash
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/review/AUTH-042 \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{"action": "approve", "release_state": "draft"}'
```

---

### POST `/v1/canonical/generate/bulk-review` — Bulk-Review

Aendert den `release_state` aller Controls, die einen bestimmten State haben.

**Request:**

```json
{
  "release_state": "needs_review",
  "action": "approve",
  "new_state": "draft"
}
```

**Beispiel:**

```bash
# Alle needs_review Controls auf draft setzen
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/bulk-review \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{"release_state": "needs_review", "action": "approve", "new_state": "draft"}'
```

---

### GET `/v1/canonical/generate/processed-stats` — Verarbeitungsstatistik

Liefert Statistiken pro RAG-Collection.

**Beispiel:**

```bash
curl https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/processed-stats \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
```

**Response:**

```json
{
  "stats": [
    {
      "collection": "bp_compliance_gesetze",
      "processed_chunks": 45200,
      "direct_adopted": 1850,
      "llm_reformed": 120,
      "skipped": 43230,
      "total_chunks_estimated": 0,
      "pending_chunks": 0
    }
  ]
}
```

---

## Kosten und Performance

### Kostenabschaetzung

| Metrik | Wert |
|--------|------|
| **Kosten pro Chunk** | ~$0.0067 (Anthropic API, Batch-Modus) |
| **Yield (Controls/Chunks)** | ~4.5-10% (nur Chunks mit konkreten Anforderungen erzeugen Controls) |
| **Vorfilter-Ersparnis** | ~55% der API-Kosten wenn aktiviert (irrelevante Chunks werden lokal aussortiert) |

### Performance-Kennzahlen

| Metrik | Wert |
|--------|------|
| **Batch-Groesse** | 5 Chunks pro API-Call (Default) |
| **API-Aufrufe Reduktion** | ~80% weniger Aufrufe durch Batching |
| **LLM-Timeout** | 180 Sekunden pro Call |
| **QA-Overhead** | ~2s pro Control (nur bei Disagreement, ~10-15% der Controls) |

### RAG Collections

| Collection | Inhalte | Erwartete Regel |
|-----------|---------|----------------|
| `bp_compliance_gesetze` | Deutsche Gesetze (BDSG, TTDSG, TKG etc.) | Rule 1 |
| `bp_compliance_datenschutz` | Datenschutz-Leitlinien + EU-Verordnungen | Rule 1/2 |
| `bp_compliance_ce` | CE/Sicherheitsstandards | Rule 1/2/3 |
| `bp_dsfa_corpus` | DSFA-Korpus | Rule 1/2 |
| `bp_legal_templates` | Rechtsvorlagen | Rule 1 |

### Aktuelle Groessenordnung

| Metrik | Wert |
|--------|------|
| RAG-Chunks gesamt | ~105.000 (nach Dedup 2026-03-16) |
| Verarbeitete Chunks | ~105.000 |
| Generierte Controls | **~4.738** |
| Konversionsrate | ~4,5% |

---

## Lizenz-Klassifikation (3-Regel-System)

Jeder Chunk wird basierend auf `regulation_code` einer Lizenzregel zugeordnet:

| Regel | Typ | Original erlaubt? | Beispiele |
|-------|-----|-------------------|----------|
| **Rule 1** (free_use) | EU-Gesetze, NIST, DE-Gesetze, Public Domain | Ja | DSGVO, BDSG, NIS2, AI Act |
| **Rule 2** (citation_required) | CC-BY, CC-BY-SA | Ja, mit Zitation | OWASP ASVS, OWASP SAMM |
| **Rule 3** (restricted) | Proprietaer | Nein, volle Reformulierung | BSI TR-03161, ISO 27001 |

### Verarbeitung nach Regel

- **Rule 1+2 → `_structure_batch()`**: Anthropic strukturiert den Originaltext als Control. Ein API-Call fuer den gesamten Batch.
- **Rule 3 → `_reformulate_batch()`**: Anthropic reformuliert vollstaendig — kein Originaltext, keine Quellennamen. Ein API-Call fuer den gesamten Batch.

### Batch Processing

Die Pipeline sammelt Chunks in Batches (Default: 5 Chunks) und sendet sie in einem einzigen Anthropic-API-Call.

1. Relevante Chunks werden mit Lizenz-Info in `pending_batch` gesammelt
2. Bei `batch_size` erreicht → `_flush_batch()`
3. Batch wird nach Lizenzregel getrennt: Rule 1+2 → `_structure_batch()`, Rule 3 → `_reformulate_batch()`
4. Ergebnis: JSON-Array mit genau N Elementen (`null` fuer irrelevante Chunks)

**Fallback:** Bei Batch-Fehler (Timeout, Parsing-Error) wird automatisch auf Einzelverarbeitung zurueckgefallen.

---

## Chunk-Tracking (Processed Chunks)

### Tabelle `canonical_processed_chunks`

| Spalte | Typ | Beschreibung |
|--------|-----|-------------|
| `chunk_hash` | VARCHAR(64) | SHA-256 Hash des Chunk-Textes |
| `collection` | VARCHAR(100) | Qdrant-Collection |
| `regulation_code` | VARCHAR(100) | Quell-Regulation (z.B. `bdsg`, `eu_2016_679`) |
| `license_rule` | INTEGER | 1, 2 oder 3 |
| `processing_path` | VARCHAR(20) | Wie der Chunk verarbeitet wurde |
| `generated_control_ids` | JSONB | UUIDs der generierten Controls |
| `pipeline_version` | SMALLINT | Pipeline-Version (1 oder 2) |
| `job_id` | UUID | Referenz auf den Generierungs-Job |

**UNIQUE Constraint:** `(chunk_hash, collection, document_version)` — verhindert Doppelverarbeitung.

### Processing Paths

| Wert | Stufe | Bedeutung |
|------|-------|-----------|
| `prefilter_skip` | 2 | Lokaler LLM-Vorfilter: Chunk nicht relevant |
| `structured` | 3a | Einzelner Chunk strukturiert (Rule 1/2) |
| `structured_batch` | 3a | Batch-Strukturierung (Rule 1/2) |
| `llm_reform` | 3b | Einzelner Chunk reformuliert (Rule 3) |
| `llm_reform_batch` | 3b | Batch-Reformulierung (Rule 3) |
| `no_control` | 3 | LLM konnte kein Control ableiten (null im Array) |
| `store_failed` | 6 | DB-Speichern fehlgeschlagen |
| `error` | — | Unerwarteter Fehler |

---

## QA Validation (Automatische Qualitaetspruefung)

Die QA-Stufe validiert die Klassifizierung jedes generierten Controls:

1. **LLM-Category:** Anthropic liefert `category` und `domain` im JSON-Response
2. **Keyword-Detection:** `_detect_category(chunk.text)` liefert eine zweite Meinung
3. **Stimmen beide ueberein?** → Schneller Pfad (kein QA noetig)
4. **Bei Disagreement:** Lokales LLM (Ollama) arbitriert
5. **Auto-Fix:** Category/Domain werden automatisch korrigiert

Die QA-Metriken werden in `generation_metadata` gespeichert:

```json
{
  "qa_category_fix": {"from": "authentication", "to": "finance", "reason": "IFRS-Thema"},
  "qa_domain_fix": {"from": "AUTH", "to": "FIN", "reason": "Finanzregulierung"}
}
```

### Recital-Erkennung (Erwägungsgrund-Detektion)

Die QA-Stufe prueft zusaetzlich, ob der `source_original_text` eines Controls tatsaechlich aus einem Gesetzesartikel stammt — oder aus einem Erwaegungsgrund (Recital). Erwaegungsgruende enthalten keine normativen Pflichten und fuehren zu falsch zugeordneten Controls.

**Erkennungsmethoden:**

| Methode | Pattern | Beispiel |
|---------|---------|----------|
| **Regex** | `\((\d{1,3})\)\s*\n` — Erwaegungsgrund-Nummern | `(126)\nUm den Verwaltungsaufwand...` |
| **Phrasen** | Typische Recital-Formulierungen (≥2 Treffer) | "daher sollte", "in Erwägung nachstehender Gründe" |

**Ergebnis bei Verdacht:**

- `release_state` wird auf `needs_review` gesetzt
- `generation_metadata.recital_suspect = true`
- `generation_metadata.recital_detection` enthaelt Details:

```json
{
  "recital_suspect": true,
  "recital_detection": {
    "recital_suspect": true,
    "recital_numbers": ["126", "127"],
    "recital_phrases": ["daher sollte"],
    "detection_method": "regex+phrases"
  }
}
```

**Funktion:** `_detect_recital(text)` in `control_generator.py`

**Hintergrund:** Bei der Analyse von ~5.500 Controls mit Quelltext wurden 1.555 (28%) als Erwaegungsgrund-Verdacht identifiziert. Der Document Crawler unterschied nicht zwischen Artikeltext und Erwaegungsgruenden, was zu falschen `article`/`paragraph`-Zuordnungen fuehrte.

### QA-Reklassifizierung bestehender Controls

```bash
# Dry Run: Welche AUTH-Controls sind falsch klassifiziert?
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/qa-reclassify \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{"limit": 50, "dry_run": true, "filter_domain_prefix": "AUTH"}'

# Korrekturen anwenden:
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/qa-reclassify \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
  -d '{"limit": 50, "dry_run": false, "filter_domain_prefix": "AUTH"}'
```

---

## Quelldateien

| Datei | Beschreibung |
|-------|-------------|
| `backend-compliance/compliance/services/control_generator.py` | 7-Stufen-Pipeline mit Batch Processing |
| `backend-compliance/compliance/api/control_generator_routes.py` | REST API Endpoints |
| `backend-compliance/compliance/services/license_gate.py` | Lizenz-Gate-Logik |
| `backend-compliance/compliance/services/similarity_detector.py` | Too-Close-Detektor (5 Metriken) |
| `backend-compliance/compliance/services/rag_client.py` | RAG-Client (Qdrant Search + Scroll) |
| `backend-compliance/migrations/046_control_generator.sql` | Job-Tracking, Chunk-Tracking Tabellen |
| `backend-compliance/migrations/048_processing_path_expand.sql` | Erweiterte Processing-Path-Werte |
| `backend-compliance/migrations/062_pipeline_version.sql` | `pipeline_version` Spalte |
| `backend-compliance/tests/test_control_generator.py` | 98+ Tests (Lizenz, Domain, Batch, Pipeline, Recital, Source-Type) |

---

## Pass 0a/0b: Atomare Control-Zerlegung

Die Pipeline v3 erweitert die 7-Stufen-Pipeline um einen Vor-Pass, der Rich Controls in atomare Controls zerlegt.

### Pass 0a: Obligation Extraction

Extrahiert individuelle normative Pflichten aus Rich Controls via LLM.

```mermaid
flowchart LR
    A[Rich Control] -->|LLM| B[Obligations]
    B --> C{Quality Gate}
    C -->|Pass| D[validated]
    C -->|Fail| E[rejected]
```

**3-Tier Klassifikation:**

| Typ | Erkennungsmuster | Beispiel |
|-----|-----------------|---------|
| **Pflicht** | muss, ist verpflichtet, hat sicherzustellen | "Der Verantwortliche MUSS ein Verzeichnis fuehren" |
| **Empfehlung** | soll, sollte, wird empfohlen | "Es SOLLTE eine Risikobewertung durchgefuehrt werden" |
| **Kann** | kann, darf, ist berechtigt | "Die Aufsichtsbehoerde KANN Geldbussen verhaengen" |

**Quality Gate (6 Regeln):**

1. Nur normative Aussagen (muss, sicherzustellen, verpflichtet)
2. Ein Hauptverb pro Obligation
3. Test-Obligations separat von operativen
4. Reporting-Obligations separat
5. Nicht auf Evidence-Ebene splitten
6. Parent-Link immer erhalten

### Pass 0b: Atomic Control Composition

Verwandelt jede validierte Obligation in ein eigenstaendiges atomares Control.

```mermaid
flowchart LR
    A[Obligation] -->|LLM| B[Atomic Control]
    B -->|Dedup Check| C{4-Stage Dedup}
    C -->|NEW| D[Insert + Index]
    C -->|LINK| E[Parent-Link]
    C -->|REVIEW| F[Review-Queue]
```

**Konfiguration:**

| Variable | Default | Beschreibung |
|----------|---------|-------------|
| `DECOMPOSITION_LLM_MODEL` | `claude-sonnet-4-6` | LLM fuer Pass 0a/0b |
| `DECOMPOSITION_BATCH_SIZE` | `5` | Obligations pro LLM-Call |
| `DECOMPOSITION_LLM_TIMEOUT` | `120` | Timeout in Sekunden |

**Ergebnisse (Stand 2026-03-21):**

| Metrik | Wert |
|--------|------|
| Rich Controls (technisch) | ~6.800 |
| Atomare Controls (bisher) | 30 (PoC: 10x CRYP, AUTH, SEC) |
| Ziel nach Full Run | ~18.000 unique Master Controls |
| Obligations pro Rich Control | ~10 |
| Dedup-Reduktion erwartet | ~70% |

### Quelldateien (Pass 0a/0b)

| Datei | Beschreibung |
|-------|-------------|
| `compliance/services/decomposition_pass.py` | Pass 0a + 0b Logik |
| `compliance/services/control_dedup.py` | 4-Stufen Dedup-Engine |
| `migrations/061_obligation_candidates.sql` | Obligation-Tabelle |
| `migrations/074_control_dedup.sql` | Dedup-Tabellen (Parent-Links, Review-Queue) |
| `tests/test_decomposition_pass.py` | 90 Tests |
| `tests/test_control_dedup.py` | 56 Tests |

---

## Verwandte Dokumentation

- [Canonical Control Library (CP-CLIB)](canonical-control-library.md) — Domains, Datenmodell, Too-Close-Detektor, CI/CD Validation
- [Deduplizierungs-Engine](dedup-engine.md) — 4-Stufen Dedup, Multi-Parent-Linking, Review-Queue
- [RAG Pipeline Benchmark](../../development/rag-pipeline-benchmark.md) — State-of-the-Art Vergleich, Optimierungsempfehlungen
- [Multi-Layer Control Architecture](canonical-control-library.md#multi-layer-control-architecture) — 10-Stage Pipeline-Erweiterung mit Obligations, Patterns, Crosswalk
