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>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user