5e5002c883
Architecture-validation finding: the implementation mode produced compliance-
flavored output ("teilweise erfüllt", "covered") from a mere customer claim,
blurring the line to the Execution layer. This is a design decision, not a text
fix — the reasoning layer judges only the customer's STATEMENT, never conformity.
- CoverageStatus -> ClaimCoverage; values are claim-relative + carry "potential":
potentially_addresses / partially_addresses / does_not_address /
insufficient_information.
- ImplementationAssessment -> ClaimObligationMapping (coverage_status ->
claim_coverage); ImplementationResponse -> ImplementationReasoningResponse
(assessments -> mappings, + explicit `disclaimer`); request renamed; engine
entry assess_implementation -> reason_implementation_claim.
- Endpoint /reasoning/implementation-assessment -> /reasoning/implementation-reasoning.
- Summary/explanations reworded: "adressiert wahrscheinlich N Pflichten … für
eine Bewertung der tatsächlichen Umsetzung sind Nachweise erforderlich (keine
Konformitätsaussage)". No "erfüllt"/"abgedeckt" leaks.
- New guard test asserts no compliance verdict leaks (no "erfüllt"; disclaimer
separates ClaimCoverage from ComplianceStatus). 23 tests green, mypy clean.
Discovery (scope/obligations) was already structurally claim-free and unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
227 lines
7.7 KiB
Python
227 lines
7.7 KiB
Python
"""Pydantic domain objects for the Regulatory Reasoning Engine.
|
|
|
|
Trigger facts that drive scope are tri-state (`Optional[bool] = None`): `None`
|
|
means "fact unknown" and produces an *uncertain* verdict plus a concrete
|
|
missing-fact prompt — never silent false security (spec §6.3).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
from typing import Dict, List, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from .enums import (
|
|
ApplicabilityStatus,
|
|
AuthorityLevel,
|
|
ClaimCoverage,
|
|
Confidence,
|
|
InterpretationVerdict,
|
|
ManufacturerRole,
|
|
MarketModel,
|
|
OverlapType,
|
|
ProductLifecyclePhase,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Input
|
|
# ---------------------------------------------------------------------------
|
|
class ProductProfile(BaseModel):
|
|
"""The customer's product / system. Tri-state booleans => unknown facts."""
|
|
|
|
product_name: str
|
|
product_profile_id: Optional[str] = None
|
|
manufacturer_role: Optional[ManufacturerRole] = None
|
|
product_type: List[str] = Field(default_factory=list)
|
|
|
|
has_software: Optional[bool] = None
|
|
has_embedded_software: Optional[bool] = None
|
|
has_remote_access: Optional[bool] = None
|
|
has_cloud_connection: Optional[bool] = None
|
|
has_ai_functionality: Optional[bool] = None
|
|
has_radio_module: Optional[bool] = None
|
|
has_safety_function: Optional[bool] = None
|
|
generates_usage_data: Optional[bool] = None
|
|
|
|
is_machine: Optional[bool] = None
|
|
is_component: Optional[bool] = None
|
|
is_spare_part: Optional[bool] = None
|
|
|
|
placed_on_market_after: Optional[date] = None
|
|
intended_use: Optional[str] = None
|
|
eu_market: Optional[bool] = None
|
|
b2b_or_b2c: Optional[MarketModel] = None
|
|
lifecycle_phase: Optional[ProductLifecyclePhase] = None
|
|
|
|
# Organisation context — only needed for NIS2 (not a product fact).
|
|
company_size: Optional[str] = None
|
|
sector: Optional[str] = None
|
|
is_essential_or_important_entity: Optional[bool] = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Scope
|
|
# ---------------------------------------------------------------------------
|
|
class ApplicableRegulation(BaseModel):
|
|
regulation_id: str
|
|
name: str
|
|
applicability_status: ApplicabilityStatus
|
|
trigger_facts: List[str] = Field(default_factory=list)
|
|
legal_basis_refs: List[str] = Field(default_factory=list)
|
|
confidence: Confidence
|
|
explanation: str
|
|
|
|
|
|
class ExcludedRegulation(BaseModel):
|
|
regulation_id: str
|
|
name: str
|
|
reason: str
|
|
|
|
|
|
class UncertainRegulation(BaseModel):
|
|
regulation_id: str
|
|
name: str
|
|
missing_facts: List[str] = Field(default_factory=list)
|
|
explanation: str
|
|
|
|
|
|
class RegulatoryScope(BaseModel):
|
|
product_profile_id: Optional[str] = None
|
|
applicable_regulations: List[ApplicableRegulation] = Field(default_factory=list)
|
|
excluded_regulations: List[ExcludedRegulation] = Field(default_factory=list)
|
|
uncertain_regulations: List[UncertainRegulation] = Field(default_factory=list)
|
|
missing_facts: List[str] = Field(default_factory=list)
|
|
confidence: Confidence = Confidence.MEDIUM
|
|
reasoning_summary: str = ""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Obligations
|
|
# ---------------------------------------------------------------------------
|
|
class ApplicableObligation(BaseModel):
|
|
obligation_id: str
|
|
title: str
|
|
source_regulation: str
|
|
legal_basis_refs: List[str] = Field(default_factory=list)
|
|
obligation_text: str
|
|
authority_level: AuthorityLevel
|
|
applies_because: List[str] = Field(default_factory=list)
|
|
applies_to_role: List[str] = Field(default_factory=list)
|
|
lifecycle_phase: List[str] = Field(default_factory=list)
|
|
overlap_group_id: Optional[str] = None
|
|
required_evidence: List[str] = Field(default_factory=list)
|
|
confidence: Confidence
|
|
# True only when obligation_id is owned by the Legal-KG registry (CRA P1).
|
|
registry_anchor: bool = False
|
|
# Machine/Data-Act obligations the registry has not canonicalised yet.
|
|
proposed: bool = False
|
|
|
|
|
|
class ObligationOverlap(BaseModel):
|
|
overlap_group_id: str
|
|
obligations: List[str] = Field(default_factory=list)
|
|
overlap_type: OverlapType
|
|
canonical_obligation_id: str
|
|
explanation: str
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Customer claims & assessments
|
|
# ---------------------------------------------------------------------------
|
|
class CustomerImplementationClaim(BaseModel):
|
|
claim_id: str
|
|
raw_statement: str
|
|
normalized_claim: str = ""
|
|
claimed_capability: List[str] = Field(default_factory=list)
|
|
related_topics: List[str] = Field(default_factory=list)
|
|
qualifiers: List[str] = Field(default_factory=list)
|
|
evidence_refs: List[str] = Field(default_factory=list)
|
|
|
|
|
|
class ClaimObligationMapping(BaseModel):
|
|
"""One row of Welt-1 reasoning: how a customer claim relates to an obligation.
|
|
|
|
Layers (spec / architect): claim -> interpretation (on the claim object) ->
|
|
*potential* obligation coverage (`claim_coverage`) -> evidence required.
|
|
Carries NO compliance verdict.
|
|
"""
|
|
|
|
claim_id: str
|
|
obligation_id: str
|
|
claim_coverage: ClaimCoverage
|
|
missing_elements: List[str] = Field(default_factory=list)
|
|
required_evidence: List[str] = Field(default_factory=list)
|
|
explanation: str
|
|
confidence: Confidence
|
|
|
|
|
|
class InterpretationAssessment(BaseModel):
|
|
interpretation_id: str
|
|
raw_interpretation: str
|
|
affected_regulations: List[str] = Field(default_factory=list)
|
|
affected_obligations: List[str] = Field(default_factory=list)
|
|
assessment: InterpretationVerdict
|
|
risks: List[str] = Field(default_factory=list)
|
|
corrected_interpretation: str = ""
|
|
legal_basis_refs: List[str] = Field(default_factory=list)
|
|
explanation: str
|
|
confidence: Confidence
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# API request / response envelopes
|
|
# ---------------------------------------------------------------------------
|
|
class ScopeRequest(BaseModel):
|
|
product_profile: ProductProfile
|
|
|
|
|
|
class ScopeResponse(BaseModel):
|
|
regulatory_scope: RegulatoryScope
|
|
missing_facts: List[str] = Field(default_factory=list)
|
|
confidence: Confidence
|
|
|
|
|
|
class ObligationsRequest(BaseModel):
|
|
product_profile: ProductProfile
|
|
regulatory_scope: Optional[RegulatoryScope] = None
|
|
|
|
|
|
class ObligationsResponse(BaseModel):
|
|
applicable_obligations: List[ApplicableObligation] = Field(default_factory=list)
|
|
overlaps: List[ObligationOverlap] = Field(default_factory=list)
|
|
excluded_obligations: List[str] = Field(default_factory=list)
|
|
evidence_for_multiple: Dict[str, List[str]] = Field(default_factory=dict)
|
|
|
|
|
|
class ImplementationReasoningRequest(BaseModel):
|
|
product_profile: ProductProfile
|
|
customer_claim: str
|
|
|
|
|
|
class ImplementationReasoningResponse(BaseModel):
|
|
claim: CustomerImplementationClaim
|
|
mappings: List[ClaimObligationMapping] = Field(default_factory=list)
|
|
missing_evidence: List[str] = Field(default_factory=list)
|
|
summary: str = ""
|
|
# Makes the Welt-1 boundary explicit: this is advisory claim-mapping, not a
|
|
# conformity verdict (that is ComplianceStatus in the Execution Graph).
|
|
disclaimer: str = ""
|
|
|
|
|
|
class InterpretationRequest(BaseModel):
|
|
product_profile: Optional[ProductProfile] = None
|
|
customer_interpretation: str
|
|
|
|
|
|
class InterpretationResponse(BaseModel):
|
|
assessment: InterpretationVerdict
|
|
affected_regulations: List[str] = Field(default_factory=list)
|
|
affected_obligations: List[str] = Field(default_factory=list)
|
|
corrected_interpretation: str = ""
|
|
risks: List[str] = Field(default_factory=list)
|
|
legal_basis_refs: List[str] = Field(default_factory=list)
|
|
explanation: str = ""
|
|
confidence: Confidence = Confidence.MEDIUM
|