from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime from typing import Optional @dataclass class Envelope: """Frozen cross-source envelope — the only schema contract locked in upfront. Additive: new fields may be added but existing ones must not be removed or renamed. All fields mirror the `envelope` table in kb-postgres (init/001_envelope.sql). """ id: str source: str # fastmail | gmail | … ts: datetime # message date, must be UTC-aware raw_ref: str # relative path to .eml in archive geo: Optional[dict] = None # null for mails; filled by layer-3 enrich entities: list = field(default_factory=list) def __post_init__(self) -> None: if self.ts.tzinfo is None: raise ValueError("Envelope.ts must be timezone-aware (UTC)")