feat(regulatory-map): customer-readable read-model over the scope (step 4)
The Map Renderer explains the engine's state, it does not extend it. Pure composition of resolve_product_scope (scope verdict) + derive_obligations (registry-linked obligations + overlaps) into one RegulatoryMap. - product_summary, trigger_facts, applicable/uncertain/excluded regulations, unsupported_domains, overlaps (shared_obligations), shared_evidence, and a customer-readable executive_summary. - No own legal decisions: applicable/uncertain mirror the scope verdict exactly. - Obligations shown ONLY when registry-linkable (registry_anchor) — MaschinenVO/ EMV obligations are proposed, so they render empty + a note, never as linked. Overlaps/shared_evidence likewise filtered to registry-linked members. - Uncertain regulations link to the navigator question that would resolve them (RED -> has_radio_module, DataAct -> generates_usage_data). - Environmental appears only as unsupported_domain; executive_summary has NO percentage (counts + "no further regulations identified" instead). - POST /reasoning/regulatory-map (thin handler). Response types are presentation- level, not meta-model classes (freeze v1.0 untouched). - 9 tests; 56 green (existing reasoning MVP stays green), mypy clean, LOC ok. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
"""Read-model for the Regulatory Map (step 4).
|
||||
|
||||
A customer-readable view that COMPOSES what the engine already computed (scope +
|
||||
obligations + overlaps). It adds no scope/obligation logic. All fields are
|
||||
application-level presentation types — NOT compliance-meta-model classes
|
||||
(architecture freeze v1.0 untouched).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from compliance.product_scope.schemas import UnsupportedDomain
|
||||
from compliance.profile.canonical import CanonicalProductRegulatoryProfile
|
||||
from compliance.reasoning.enums import AuthorityLevel, Confidence
|
||||
|
||||
|
||||
class RegulatoryMapRequest(BaseModel):
|
||||
product_profile: CanonicalProductRegulatoryProfile
|
||||
|
||||
|
||||
class ObligationRef(BaseModel):
|
||||
obligation_id: str
|
||||
title: str
|
||||
legal_basis_refs: List[str] = Field(default_factory=list)
|
||||
authority_level: AuthorityLevel
|
||||
|
||||
|
||||
class ApplicableRegulationView(BaseModel):
|
||||
regulation_id: str
|
||||
name: str
|
||||
why_applicable: str
|
||||
triggered_by: List[str] = Field(default_factory=list)
|
||||
obligations: List[ObligationRef] = Field(default_factory=list)
|
||||
obligations_note: str = "" # set when obligations are not yet registry-linkable
|
||||
confidence: Confidence
|
||||
|
||||
|
||||
class UncertainRegulationView(BaseModel):
|
||||
regulation_id: str
|
||||
name: str
|
||||
missing_facts: List[str] = Field(default_factory=list)
|
||||
question_refs: List[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ExcludedRegulationView(BaseModel):
|
||||
regulation_id: str
|
||||
name: str
|
||||
exclusion_reason: str
|
||||
|
||||
|
||||
class OverlapView(BaseModel):
|
||||
overlap_group_id: str
|
||||
shared_obligations: List[str] = Field(default_factory=list)
|
||||
explanation: str = ""
|
||||
|
||||
|
||||
class RegulatoryMap(BaseModel):
|
||||
scope_resolved: bool
|
||||
product_summary: str
|
||||
trigger_facts: List[str] = Field(default_factory=list)
|
||||
applicable_regulations: List[ApplicableRegulationView] = Field(default_factory=list)
|
||||
uncertain_regulations: List[UncertainRegulationView] = Field(default_factory=list)
|
||||
excluded_regulations: List[ExcludedRegulationView] = Field(default_factory=list)
|
||||
unsupported_domains: List[UnsupportedDomain] = Field(default_factory=list)
|
||||
overlaps: List[OverlapView] = Field(default_factory=list)
|
||||
shared_evidence: Dict[str, List[str]] = Field(default_factory=dict)
|
||||
executive_summary: str = ""
|
||||
Reference in New Issue
Block a user