from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional
@dataclass
@dataclass
[docs]
class CrossPatternLink:
"""A single link between one HistoricalSignalEpisode and one HistoricalDocExtraction.
Carries full provenance so analysts can trace exactly which checks passed
(direct reference, temporal, or semantic/FM; asset compatibility is scored
separately as asset_match).
"""
[docs]
time_overlap_hours: Optional[float]
[docs]
temporal_link_skipped: bool
[docs]
linkage_precedence_level: int # 1=direct, 2=temporal, 3=semantic/FM
[docs]
component_overlap: List[str]
[docs]
fm_alignment_score: Optional[float]
[docs]
signal_similarity_score: float
[docs]
document_similarity_score: Optional[float]
[docs]
provenance: Dict[str, Any]
@dataclass
[docs]
class CandidateCrossPatternEvidence:
"""Aggregated cross-pattern evidence for a single causality candidate.
Produced by CrossPatternLinker.run() — one instance per candidate in
causality_candidates["candidates"].
"""
[docs]
linked_episode_ids: List[str]
[docs]
linked_doc_ids: List[str]
[docs]
support_posture: str # "reinforcing" | "conflicting" | "weakly_supporting" | "unresolved"
[docs]
reinforcement_strength: Optional[str] # "single" | "multiple_consistent" | "mixed" | None
[docs]
linkage_outcome: str # "linked" | "no_data" | "no_match" | "below_threshold"
[docs]
evidence_paths: List[CrossPatternLink]