Files
breakpilot-compliance/docs-src/development/obligation_registry_v1.md
T
Benjamin Admin 62da36872f docs(citability): generische Legal-Reference-Hülle als Phase-B-Design-Vorgabe
User-Ergänzung: norm_ids sind nur die erste Rechtsreferenz-Art. Runtime-Vertrag (Phase B)
soll eine erweiterbare Hülle tragen (legal_reference: {norm_ids, citation_units, recital_ids,
guidance_ids, case_law_ids, interpretation_ids}), damit neue Referenz-Arten additiv als
optionale Keys landen ohne erneuten Vertrags-/Go-Struct-Umbau. Naming-Hinweis: NICHT
"legal_basis" (kollidiert mit dem Obligation-Array). Bindend ⊥ Guidance bleibt erhalten.
Phase B baut NUR norm_ids; Rest = reservierter Platz, kein Bauauftrag. Nur Spec, kein Code.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-01 14:35:56 +02:00

202 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Obligation Registry v1 — Schema, Zitierfähigkeit, Zwei-Graphen-Architektur
Status: **Spec festgeschrieben (2026-06-24)**. Baut auf
[legal_obligation_layer_v1.md](legal_obligation_layer_v1.md) +
[obligation_aggregation_validation.md](obligation_aggregation_validation.md).
Die Obligation Discovery Pipeline v1 ist gegen Ground Truth validiert
(SBOM 12 vs 10, Vuln 8 vs 7, out_of_scope + conditional Applicability korrekt).
## Leitsatz
**Die Legal Obligation ist das fachliche Wissensobjekt der Plattform** — nicht der Master
Control. Controls sind Prüfstrategien / Erkennungsmuster / Evidenzsammler FÜR eine Obligation.
Ohne Zitierfähigkeit ist die Registry fachlich nicht belastbar: die erste Kundenfrage ist
immer „**Wo steht das?**".
## Zwei Assets, zwei Graphen, EIN Join (nicht verschmelzen, verbinden)
- **Asset 1 — Compliance Knowledge** (bereits gebaut): 313k atomare Controls, 33k Master
Controls, ~14k use-case-gemappt, Dedup, Obligation Layer, Applicability, Tiering, G/C/E.
- **Asset 2 — Zitierfähige Wissensbasis** (entsteht in anderer Session): Dokument → Chunk →
Paragraph → Span → Zitat.
Die beiden werden **NICHT verschmolzen** (das wäre wie eine normalisierte DB nach CSV zu
exportieren und neu zu importieren). Sie werden über die **Obligation gekoppelt**:
```
GRAPH 1 — Legal Knowledge Graph (Chat/Advisor) GRAPH 2 — Compliance Execution Graph (Engine)
Regulation → Annex/Artikel → Paragraph → Span Obligation → Control → Criterion → Evidence → Finding
\ /
\____ LEGAL OBLIGATION ______/ ← gemeinsame Sprache (der Join)
```
Chat: „diese Aussage stammt aus Absatz X." · Engine: „diese Obligation ist nicht erfüllt." →
beide meinen DIESELBE `obligation_id`.
## Registry-Schema v1
```yaml
id: # snake_case, regulierungs-agnostisch (z.B. sbom_complete)
name: # kurz
description: # 1 Satz
tier: # LEGAL_MINIMUM | BEST_PRACTICE | IMPLEMENTATION_GUIDANCE | EVIDENCE
family: # Organisationshilfe (z.B. sbom, vulnerability_handling)
applicability: # universal | conditional:<pred> | domain:<x>
facets: # welche Evidenz-Facetten die Pflicht belegt
governance: bool
capability: bool
evidence: bool
legal_basis: # PRIMÄRRECHT — Pflicht zwingend (mind. 1 Anker für LEGAL_MINIMUM)
- source: CRA
regulation_code: eu_2024_2847
article: "" # falls zutreffend
annex: "Annex I, Part II"
section: ""
paragraph: ""
span_id: "" # harter Anker in die zitierfähige Wissensbasis (Asset 2)
document_version: ""
citation: "" # menschenlesbar
guidance_basis: # SEKUNDÄR — Umsetzung/Best Practice, NICHT Pflicht
- source: NIST SSDF
anchor: ""
role: best_practice # implementation_guidance | best_practice
member_controls: # control_uuids (Prüflogik aus Asset 1)
citation_anchor_ids: # span/paragraph-Anker (Asset 2) — auf der OBLIGATION, NICHT auf Controls
relationships: # siehe Beziehungsgraph
decision_method: # CONTENT/LLM | CONTENT/EMBEDDING | FIELD/REGEX | BEHAVIOR/PLAYWRIGHT ...
out_of_scope: [] # ausgeschlossene Cluster + Begründung
```
## Zitierfähigkeit hängt an der OBLIGATION (nicht an Controls)
258 SBOM-Controls → 11 Obligations: nur die **Obligation** speichert
`CRA / Annex I / Paragraph X / chunk_id / span_id / document_version`. Die 258 Controls zeigen
nur auf die `obligation_id`. Folge: **Regulierungsänderung (CRA v1→v2) = `citation_anchor`
tauschen, Controls bleiben identisch.** Massive Pflegeersparnis + Versionsstabilität.
## `legal_basis` vs `guidance_basis` + `source_role`
Damit beim Verschmelzen von CRA + NIST + OWASP zu einer Obligation NICHT verloren geht, was
Pflicht / Best Practice / Evidenz / Umsetzung ist, klassifiziert die Discovery-Pipeline jeden
Member/Cluster mit einer **`source_role`**:
```
LEGAL_BASIS → Primärrecht (begründet die Pflicht)
GUIDANCE → NIST/OWASP/ENISA/BSI/ISO (Umsetzung/Best Practice)
EVIDENCE → Nachweis/Bericht/Audit
IMPLEMENTATION → technische Umsetzungsanweisung
OUT_OF_SCOPE → gehört nicht zur Obligation (andere Regulierung/Domäne)
```
## HARTE Tier-Regel
Eine Obligation wird **`LEGAL_MINIMUM` nur mit mindestens einem Primärrechts-Anker**
(`legal_basis` nicht leer). Ohne Primärrechts-Anker:
`BEST_PRACTICE | IMPLEMENTATION_GUIDANCE | EVIDENCE`**aber niemals Pflicht.**
## Beziehungsgraph (Ontologie)
**Strukturell** (bereits in der Pipeline): `same_obligation`, `sub_obligation`,
`applicability_variant`, `evidence_for`, `governance_for`, `out_of_scope`.
**Semantisch (NEU, P2-Ergänzung):** `requires`, `implements`, `supports`,
`produces_evidence_for`, `depends_on`, `derived_from`. Beispiele:
```
sbom_established --supports--> vulnerability_handling --supports--> incident_reporting
authentication --requires--> credential_management
```
→ für den Compliance Advisor extrem wertvoll (er kann Pflicht-Ketten erklären).
## Citation-Anchor-Pipeline (Document → Obligation, NICHT Document → Control)
Der neue Ingest erzeugt zusätzlich zu Chunk/Embedding: `paragraph_uuid`, `span_uuid`,
`document_version`, `legal_citation`, `referenced_articles`, `referenced_regulations`.
**Erst danach** läuft Obligation Discovery, sodass jede neu entdeckte Obligation sofort ihre
Primärquelle bekommt:
```
Neue Dokumente → Chunking → Span IDs → LLM („welche Obligation(en)?") → Confidence
→ Review → obligation.citation_anchor_ids[]
```
Die alten Controls werden wiederverwendet; die Pipeline erzeugt zusätzlich Obligation→Evidence
und Obligation→Citation-Anchors. **Kein Re-Ingest zum Neubau von Controls.**
## Sequenz (geändert — Registry vor weiteren Cuts)
```
SBOM ✓ → Vuln ✓ → Registry v1 (DIESE Spec) → Ontologie/Beziehungsgraph ergänzen
→ Authentication → Remote Access → Logging → Updates
```
Begründung: Schema jetzt billig änderbar; bei 3001000 Obligations wird jede Schemaänderung
teuer. Fortschritt wird daran gemessen, ob jede neue Obligation die Registry besser macht —
nicht an neuen Controls.
## Scope-Audit (Review-Step, PFLICHT je Cut)
Die Registry modelliert **Hersteller-Pflichten**. Bestimmungen, die an Behörden / notifizierte
Stellen / Mitgliedstaaten adressiert sind (Sanktionen, Marktüberwachung, Anforderungen an
Konformitätsbewertungsstellen), sind Enforcement-/Institutions-Recht. **Prinzip: Adressat der Norm
⊥ Handlungspflicht des Herstellers.** `scope`-Attribut-Achse (Enum, KEINE neue Objektklasse):
- `in_scope` — Norm adressiert direkt den Hersteller (Default).
- `out_of_scope` — reines Staats-/Durchsetzungs-/Institutions-Recht (Adressat ≠ Hersteller, KEINE
mittelbare Herstellerpflicht). Aus `obligation_join_keys.json` gefiltert. Präzedenz CSIRT/ENISA.
- `derived_obligation` — Norm adressiert primär eine andere Rolle, erzeugt aber MITTELBAR eine
Hersteller-Handlungspflicht → **bleibt im Set** (`scope_split_candidate` markiert spätere
Aufspaltung Normadressat ↔ abgeleitete Pflicht; nicht vorzeitig festziehen).
**Gate-Regel:**
```
Jeder neue Obligation-Cut muss durch Scope-Audit laufen.
Findings mit authority-/institution-addressed obligations werden dokumentiert.
Automatische Reclassification ist verboten, solange kein explizites Review-Go vorliegt.
```
**Werkzeug-Trennung (FLAG ⊥ MUTATE):**
- `scope_audit.py`**flaggt nur** (scannt alle Registries → `scope_audit_findings.json`; mutiert nie).
- `validate_registry.py` — surfaced pro Cut unklassifizierte authority-/institution-Obligations als
**non-fatal Warnung** (blockt nicht, mutiert nicht).
- `apply_scope_classification.py`**mutiert** (setzt `scope`), läuft NUR nach explizitem Review-Go
(ändert `join_keys` + Compliance-Execution-Sync → menschlich/koordiniert).
## Weg 1 — Obligation→Norm-Zitierfähigkeit (Datenbereitschaft; UI deferred)
**Zwei Zitierebenen — NICHT verwechseln (beide langfristig nötig):**
- **RAG-Evidence-Zitat** (heute im Advisor sichtbar): `Frage/Antwort → Evidence-Chunk`
(KB-v2 `article_label`/`source_url`, `[n]`-Citations). Quelle = Compliance/KB-Session `/retrieve`.
- **Obligation→Norm-Zitat** (der `norm_id`-Join): `Pflicht → konkrete Rechtsgrundlage` (KB-v2-Unit).
Quelle = diese Registry. **Weg 1** = dieses Zitat sichtbar machen.
**Datenbereitschaft (Stand 2026-07-01):**
| Baustein | Status |
|---|---|
| `obligations/*.json``legal_basis.norm_ids` | ✅ 62/64 joinbar (53 annex + 16 article, KB-v2-verifiziert) |
| KB-v2-Join-Ziele (CRA Art1-71 · MaschVO Art1-54 · Annexe) | ✅ bestätigt |
| `obligation-status`-Endpoint + Traversal (`obligation_id→citation_unit→Controls→Evidence`) | ✅ vorhanden; exponiert `LegalBasis = citation_units` |
| Runtime-Vertrag `obligation_join_keys.json` trägt `norm_ids` | ❌ nur `citation_units` (Anker-Strings), KEINE `norm_ids` |
| Go `ObligationKey`-Struct / `CitationSpans` | ❌ kein `NormIDs`-Feld; `AssessObligationStatus` setzt `CitationSpans:"pending"` hart (compliance_status.go) |
**Deferred-Sequenz (UI zuletzt) — bewusst NICHT gebaut:**
1. **Data-Prep** (nicht-UI, Domäne 2): `norm_ids` in `export_join_keys.py``obligation_join_keys.json` exportieren.
2. **Build** (ai-sdk, koordiniert): `NormIDs` in `ObligationKey`; `AssessObligationStatus` füllt `CitationSpans` aus `norm_ids` statt `"pending"`.
3. **UI** (später): „Diese Pflicht beruht auf **CRA Anhang I / Art. 13**".
**Design-Vorgabe für Phase B (Runtime-Vertrag): generische Legal-Reference-Hülle statt flachem
`norm_ids`.** `norm_ids` sind nur die ERSTE Rechtsreferenz-Art. Damit der Runtime-Vertrag
(`obligation_join_keys.json` + Go `ObligationKey`) bei neuen Referenz-Arten NICHT erneut geändert
werden muss, trägt jeder Eintrag eine **erweiterbare Hülle** (optionale Keys, additiv):
```json
"legal_reference": { // Hülle — NICHT "legal_basis" (kollidiert mit dem Obligation-Array)
"norm_ids": [...], // bindendes Primärrecht (Artikel/Anhang) ← Phase B baut nur DAS
"citation_units": [...], // menschliche Anker-Strings (Interim-Brücke)
"recital_ids": [...], // Erwägungsgründe (später, additiv)
"guidance_ids": [...], // Leitlinien EDPB/DSK (später, additiv)
"case_law_ids": [...], // Gerichtsurteile (später, additiv)
"interpretation_ids": [...] // Interpretationshilfen (später, additiv)
}
```
Prinzip: neue Referenz-**Art** = neuer optionaler Key in der Hülle → Vertrag/Go-Struct bleiben
stabil (wie die `scope`-Achse: erweitern über Attribute, nicht über Strukturumbau). **Bindend ⊥
Guidance-Trennung bleibt erhalten** (`norm_ids`/`case_law` = bindend · `guidance_ids` = Soft-Law) —
konsistent mit `legal_basis``guidance_basis` und dem Authority-Router. In Phase B wird NUR
`norm_ids` befüllt; die übrigen Keys sind reservierter Platz, kein Bauauftrag.