# Lessons Learned — MC `check_type` Klassifikation (KRITISCH fuer CRA + alle neuen Frameworks)

Datum: 2026-05-17
Auslöser: Compliance-Check BMW lieferte 0/381 Cookie-MCs, 3/75 Impressum-MCs, 43/571 DSE-MCs — alle Doc-Typen unter 20%.

## TL;DR

**Die heutigen Master-Controls (MCs) vermischen drei strukturell unterschiedliche Klassen von Pruefungen in einer einzigen Tabelle (`compliance.doc_check_controls`). Nur eine der drei Klassen lässt sich gegen Dokument-Text matchen. Die anderen zwei werden faelschlich als "failed" gezaehlt, weil sie ueberhaupt nicht ueber Text-Matching gepruefbar sind.**

Bei der CRA-MC-Generierung (laeuft jetzt im Pass 0a mit Haiku) **MUSS** jeder MC ein **`check_type`-Feld** bekommen, bevor er in die Datenbank geht. Sonst wiederholt sich das Problem.

## Die drei Klassen

| `check_type` | Pruefungsfrage-Pattern | Beispiel | Wie pruefbar? |
|---|---|---|---|
| **`text`** | "Enthaelt das Dokument...", "Wird im X die Y benannt?", "Ist im Text aufgelistet..." | "Wird im Impressum die Aufsichtsbehoerde benannt?" | Regex / Embedding-Match gegen Doc-Text |
| **`process`** | "Ist sichergestellt...", "Ist implementiert...", "Wird durchgefuehrt..." | "Ist sichergestellt, dass Cookies erst nach Einwilligung gespeichert werden?" | Evidence/TOM-Check — kein Doc-Text vorhanden |
| **`review`** | "Sind ALLE / Werden ALLE / Stimmt X mit Y ueberein?" | "Sind alle Verarbeitungszwecke vollstaendig erfasst?" | Mensch (DSB) — Checkliste, nicht automatisch |

## Befund aus den BMW-Daten

| Doc-Type | TEXT (matchbar) | PROCESS | UNKLAR/REVIEW | Total | % TEXT |
|---|---|---|---|---|---|
| cookie | 30 | 49 | 302 | 381 | **8%** |
| dse | 72 | 139 | 359 | 571 | **13%** |
| impressum | 14 | 14 | 47 | 75 | **19%** |
| agb | 24 | 20 | 69 | 113 | 21% |
| widerruf | 29 | 26 | 96 | 153 | 19% |
| loeschkonzept | 38 | 39 | 232 | 309 | 12% |

**Selbst mit perfektem Matching liegt die Obergrenze fuer doc_check bei 8-20%**, weil 80-92% der MCs nicht ueber Text-Matching pruefbar sind. Es sind keine "schlechten MCs" — sie sind in der falschen Schublade.

## Konsequenzen fuer CRA-Generation (Pass 0a)

### 1. Prompt-Aenderung (Hauptmassnahme)

Der Pass-0a-Prompt fuer Haiku/Sonnet MUSS pro generiertem Control ein `check_type`-Feld erzwingen. Vorschlag:

```jsonc
{
  "control_id": "CRA-...-A01",
  "title": "...",
  "check_question": "...",
  "check_type": "text" | "process" | "review",   // PFLICHT
  "rationale_for_check_type": "..."
}
```

Klassifikations-Regel im Prompt:

> Wenn deine `check_question` mit "Enthaelt", "Wird … genannt/aufgelistet/erwaehnt", "Steht im Text" beginnt -> `text`.
> Wenn sie mit "Ist sichergestellt", "Ist implementiert", "Wird durchgefuehrt", "Existiert ein Prozess" beginnt -> `process`.
> Wenn sie mit "Sind ALLE", "Werden ALLE", "Stimmt X mit Y ueberein" beginnt -> `review`.
> Im Zweifel: lieber `review` als `text`.

### 2. Doc-Type-Zuordnung kritisch validieren

Bei den heutigen MCs sind viele falsch zugeordnet (z.B. "Bestellbestätigung implementieren" landet im `impressum`-doc_type, gehoert aber zu AGB/Widerruf). Fuer CRA:

- **`doc_type` darf nur Werte aus einer expliziten Liste annehmen** — pro Regulation festlegen.
- Fuer CRA z.B.: `produkt_konformitaetserklaerung`, `risiko_management_dossier`, `sbom`, `cra_dse`, `meldepflichten_doku`.
- Falsche Zuordnung im Prompt explizit verbieten: "Wenn der Control nicht eindeutig zu EINEM dieser Doc-Typen passt, setze `doc_type: 'unassigned'` und `check_type: 'review'`."

### 3. Zwei Tabellen statt einer

Heutige Architektur:
- `compliance.doc_check_controls` <- alle 1874 MCs (mit allem vermischt)

Empfohlen fuer CRA + Refactor:
- `compliance.text_check_controls` <- nur `check_type='text'`
- `compliance.process_check_controls` <- nur `check_type='process'`, gepruefte via Evidence/TOM
- `compliance.review_checklist_controls` <- nur `check_type='review'`, gepruefte via DSB-Workflow

Falls Schema-Aenderung nicht moeglich (CLAUDE.md: DB ist frozen), Sidecar-SQLite mit `mc_classification.db` oder neue Spalte als Add-only-Migration.

### 4. Dedupe-Phase respektieren

In Pass 0b (Dedup) muss `check_type` ein **Pflicht-Dedupe-Key** sein:
- Zwei MCs mit gleicher Aussage aber unterschiedlichem `check_type` sind **nicht** Duplikate — sie pruefen verschiedene Dinge ("ist im Text genannt" vs "ist technisch implementiert").
- Heute werden solche faelschlich gemerged → noch mehr Vermischung.

### 5. Matching-Engine danach umbauen

Das eigentliche doc-check-Match-System muss nur noch `check_type='text'`-MCs verarbeiten. Andere werden in ihre eigenen Module geroutet:

- `text` MCs -> `rag_document_checker` (Regex + spaeter Embedding)
- `process` MCs -> neuer `evidence_check_runner` (Kunde lieferte Nachweise/TOM hoch)
- `review` MCs -> neuer `review_checklist_ui` (DSB beantwortet manuell)

## Checkliste fuer CRA-Session

- [ ] Pass-0a-Prompt um `check_type`-Pflichtfeld erweitert (Wortlaut-Regel + Beispiele)
- [ ] Pass-0a-Prompt zwingt `doc_type` aus expliziter Whitelist
- [ ] Pass-0b-Dedup-Key enthaelt `check_type`
- [ ] Output-Validator weist MCs ohne `check_type` zurueck
- [ ] DB-Schema (oder Sidecar) hat `check_type`-Spalte mit Default `review` (sicherer Fallback)
- [ ] Stichprobe von 50 generierten CRA-MCs vor Bulk-Run: TEXT-Anteil sollte 30-50% sein (mehr als bei den alten DSGVO-MCs, weil CRA stark dokument-fokussiert ist).

## Update 2026-05-17 — Parallel-CRA-Session-Findings

Die laufende CRA-Generation hat ein Feld `verification_method` (document/tool/hybrid/code_review/empty), das **NICHT identisch** mit `check_type` ist:

- `verification_method` fragt: **WAS schaust du dir an?** (Dokument, Tool-Output, Code)
- `check_type` fragt: **KANN das per Text-Match geprueft werden?** (text/process/review)

Ein Control kann `verification_method=document` haben UND trotzdem `check_type=process` sein. Beispiel: "Wird die SBOM regelmaessig (mindestens monatlich) aktualisiert?" — Du schaust ins Dokument SBOM-Historie, prüfst aber einen Prozess. Text-Match findet das nie.

**Mapping-Heuristik (gut genug fuer 80% der Faelle, Rest LLM):**

| `verification_method` | Auto-Mapping `check_type` | LLM noetig? |
|---|---|---|
| `tool` | `process` | nein |
| `code_review` | `process` | nein |
| empty/null | `review` (sicherer Default) | nein |
| `document` | erstmal `text`, Stichprobe pruefen | 10-20% sampling |
| `hybrid` | LLM klassifizieren | ja, alle |

**Idealfall (fuer alle KUENFTIGEN Pass-0a-Generationen — auch CRA falls man nochmal generiert):** Beide Felder gleichzeitig generieren, nicht eins aus dem anderen ableiten.

## Backfill-Workflow fuer die laufende CRA-Generation

1. Aktueller Haiku-Job laeuft fertig (kein Restart, kein Verlust)
2. Nach Job-Ende: Auto-Mapping fuer eindeutige Buckets (tool/code_review/empty)
3. Sonnet-Klassifikation nur fuer `document`+`hybrid` Subset (~62 Calls fuer 1500 Controls, ~$0.05 statt $2)
4. Wiederverwenden: `breakpilot-compliance/backend-compliance/scripts/classify_mc_check_type.py` — nur DB-Query anpassen (Source-Tabelle + WHERE-Filter)
5. Validierung: TEXT-Anteil bei CRA sollte 40-60% sein (CRA ist dokument-zentrierter als DSGVO-Cookie)

## Quervewweise

- BMW-Run-Befund: `breakpilot-compliance` E-Mail vom 2026-05-17, check_id `08bcc9dd`
- Bestehender Klassifikations-Skript fuer Retrofit der alten 1874: `backend-compliance/scripts/classify_mc_check_type.py`
- Doc-Type-Audit-Query: dieselbe Datei, am Ende
