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

11 KiB
Raw Blame History

Obligation Registry v1 — Schema, Zitierfähigkeit, Zwei-Graphen-Architektur

Status: Spec festgeschrieben (2026-06-24). Baut auf legal_obligation_layer_v1.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

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.

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 | EVIDENCEaber 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.pyflaggt 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.pymutiert (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/*.jsonlegal_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.pyobligation_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):

"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_basisguidance_basis und dem Authority-Router. In Phase B wird NUR norm_ids befüllt; die übrigen Keys sind reservierter Platz, kein Bauauftrag.