Files
breakpilot-compliance/backend-compliance/compliance/rci/schemas.py
T
Benjamin Admin a5687bbc65 feat(rci): Regulatory Change Intelligence foundation (delta over the stored map)
RCI/Delta as a read-/reasoning layer ON TOP of the product-first pipeline. Answers
"what changes relative to my existing Regulatory Map?" — NOT "what does the new
law say in general". No UI, no ingestion (newsletter/mailbox), no RAG, no new
regulations/controls, no legal evaluation outside the stored map.

- 4 core objects (compliance/rci/schemas.py): ComplianceBaseline (snapshot of
  profile + map + registry obligations + required/present evidence), RegulatoryChange
  (simulated/provided INPUT), ObligationDelta (delta_type NEW|CHANGED|REMOVED|
  ALREADY_COVERED|NEEDS_REVIEW|NOT_APPLICABLE), ChangeImpactSummary. delta_type is a
  THIRD vocabulary, disjoint from ClaimCoverage (Welt 1) and ComplianceStatus (Welt 2).
- create_baseline() snapshots the existing pipeline once; assess_change() computes
  deltas deterministically against the snapshot (no re-evaluation).
- 12 tests = the 5 acceptance questions (affects product? new/changed? already
  covered by evidence? needs human review? not relevant?) + repeal/uncertain-reg/
  missing-evidence/boundary. Existing pipeline tests stay green; mypy clean; LOC ok.
- App/reasoning types only — no compliance-meta-model classes (freeze v1.0 untouched).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 13:45:23 +02:00

93 lines
4.1 KiB
Python

"""Regulatory Change Intelligence (RCI) — domain objects.
RCI is a read-/reasoning layer ON TOP of the product-first pipeline. It answers
"what changes relative to my existing Regulatory Map?" — NOT "what does the new
law say in general". A RegulatoryChange is simulated/provided INPUT (no ingestion,
no newsletter/mailbox, no RAG); the delta is computed against a stored
ComplianceBaseline (snapshot of the map).
`delta_type` is a THIRD vocabulary — distinct from `ClaimCoverage` (Welt 1, what
the customer claims) and `ComplianceStatus` (Welt 2, verified evidence). The three
must never be conflated. These are application/reasoning types, NOT
compliance-meta-model classes (architecture freeze v1.0 untouched).
"""
from __future__ import annotations
from enum import Enum
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
from compliance.profile.canonical import CanonicalProductRegulatoryProfile
from compliance.reasoning.enums import AuthorityLevel, Confidence
from compliance.regulatory_map.schemas import RegulatoryMap
class DeltaType(str, Enum):
NEW = "new" # obligation now applies that was not in the baseline
CHANGED = "changed" # existing obligation substantively modified
REMOVED = "removed" # obligation no longer applies (repeal)
ALREADY_COVERED = "already_covered" # existing obligation, evidence likely suffices
NEEDS_REVIEW = "needs_review" # a human must check
NOT_APPLICABLE = "not_applicable" # change does not touch this product's map
class ChangeType(str, Enum):
NEW_REGULATION = "new_regulation"
AMENDMENT = "amendment"
REPEAL = "repeal"
GUIDANCE_UPDATE = "guidance_update"
# ── stored snapshot ──────────────────────────────────────────────────────
class ComplianceBaseline(BaseModel):
baseline_id: str
product_profile_snapshot: CanonicalProductRegulatoryProfile
regulatory_map_snapshot: RegulatoryMap
applicable_obligations: List[str] = Field(default_factory=list) # registry-linked obligation_ids
# required evidence per obligation (derived) — to compute missing_evidence
obligation_evidence_required: Dict[str, List[str]] = Field(default_factory=dict)
# evidence the customer ALREADY has, per obligation (provided)
evidence_refs: Dict[str, List[str]] = Field(default_factory=dict)
created_at: Optional[str] = None
# ── simulated/provided change (INPUT — never ingested) ───────────────────
class RegulatoryChange(BaseModel):
change_id: str
source: str = "simulated"
affected_regulations: List[str] = Field(default_factory=list)
affected_obligations: List[str] = Field(default_factory=list)
change_type: ChangeType
effective_date: Optional[str] = None
authority_level: AuthorityLevel = AuthorityLevel.LEGAL_TEXT
summary: str = ""
# ── per-obligation delta ─────────────────────────────────────────────────
class ObligationDelta(BaseModel):
obligation_id: str
delta_type: DeltaType
reason: str
affected_evidence: List[str] = Field(default_factory=list) # evidence already present for it
missing_evidence: List[str] = Field(default_factory=list) # required but not yet present
confidence: Confidence
# ── management-level summary ──────────────────────────────────────────────
class ChangeImpactSummary(BaseModel):
what_changed: str = ""
what_matters_for_this_product: List[str] = Field(default_factory=list) # need action
already_covered: List[str] = Field(default_factory=list)
needs_review: List[str] = Field(default_factory=list)
not_relevant: List[str] = Field(default_factory=list)
unsupported_domains: List[str] = Field(default_factory=list)
class ChangeAssessment(BaseModel):
change_id: str
affects_product: bool
deltas: List[ObligationDelta] = Field(default_factory=list)
summary: ChangeImpactSummary