"""Intent-neutral provenance verification. Model output is untrusted input. Canonical source texts come only from a locally built SourceRegistry. Downstream stages consume a locally materialized VerifiedArtifact. This module has no journal, dialogue, or product-intent vocabulary. """ from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Mapping COVERAGE_ALL_SELECTED_SOURCES = "all_selected_sources" COVERAGE_SELECTED_EVIDENCE = "selected_evidence" COVERAGE_MODES = frozenset({COVERAGE_ALL_SELECTED_SOURCES, COVERAGE_SELECTED_EVIDENCE}) ARTIFACT_KIND = "verified_artifact" class ProvenanceError(Exception): """Fail-closed verification error. Domain mapping happens at the caller boundary.""" def __init__(self, reason: str, **details: Any): super().__init__(reason) self.reason = reason self.details = details @dataclass(frozen=True) class SourceRecord: source_id: str role: str text: str metadata: Mapping[str, Any] = field(default_factory=dict) class SourceRegistry: """Local authority for original content. Model text never replaces these records.""" def __init__(self, sources: list[SourceRecord] | None = None): self._sources: dict[str, SourceRecord] = {} self._order: list[str] = [] for source in sources or []: self.add(source) def add(self, source: SourceRecord) -> None: source_id = (source.source_id or "").strip() if not source_id: raise ProvenanceError("missing_source_id") if source_id in self._sources: raise ProvenanceError("duplicate_source_id", source_id=source_id) self._sources[source_id] = SourceRecord( source_id=source_id, role=source.role, text=source.text, metadata=dict(source.metadata or {}), ) self._order.append(source_id) def get(self, source_id: str) -> SourceRecord | None: return self._sources.get(source_id) def order(self) -> list[str]: return list(self._order) def items(self) -> list[SourceRecord]: return [self._sources[source_id] for source_id in self._order] @dataclass(frozen=True) class EvidenceRef: source_id: str excerpt: str @dataclass(frozen=True) class VerifiedEvidence: source_id: str excerpt: str start: int end: int @dataclass(frozen=True) class VerificationPolicy: allowed_roles: frozenset[str] coverage: str = COVERAGE_ALL_SELECTED_SOURCES selected_ids: tuple[str, ...] = () source_order: tuple[str, ...] = () include_unverified_annotations: bool = False @dataclass(frozen=True) class VerifiedArtifact: coverage: str source_order: tuple[str, ...] sources: tuple[dict[str, str], ...] evidence: tuple[VerifiedEvidence, ...] annotations_unverified: tuple[dict[str, Any], ...] = () def texts(self) -> list[str]: return [item.get("text") or "" for item in self.sources if item.get("text")] def to_payload(self) -> dict[str, Any]: payload: dict[str, Any] = { "kind": ARTIFACT_KIND, "coverage": self.coverage, "source_order": list(self.source_order), "order": [item.get("source_id") for item in self.sources], "sources": [dict(item) for item in self.sources], "evidence": [ { "source_id": item.source_id, "excerpt": item.excerpt, "start": item.start, "end": item.end, } for item in self.evidence ], } if self.annotations_unverified: payload["annotations_unverified"] = [dict(item) for item in self.annotations_unverified] return payload def locate_excerpt(text: str, excerpt: str) -> tuple[int, int] | None: """Local span from a confirmed literal excerpt. Model offsets are ignored.""" if not excerpt: return None start = (text or "").find(excerpt) if start < 0: return None return start, start + len(excerpt) def texts_from_payload(data: Mapping[str, Any] | None) -> list[str]: if not isinstance(data, Mapping): return [] parts: list[str] = [] for item in data.get("sources") or []: if isinstance(item, Mapping) and item.get("text"): parts.append(str(item["text"])) return parts def verify_evidence( registry: SourceRegistry, ref: EvidenceRef, policy: VerificationPolicy, ) -> VerifiedEvidence: source_id = (ref.source_id or "").strip() excerpt = ref.excerpt or "" if not source_id: raise ProvenanceError("missing_source_id") if not excerpt.strip(): raise ProvenanceError("unverified_excerpt", source_id=source_id) if policy.selected_ids and source_id not in policy.selected_ids: raise ProvenanceError("unknown_source", source_id=source_id) source = registry.get(source_id) if source is None: raise ProvenanceError("unknown_source", source_id=source_id) if source.role not in policy.allowed_roles: raise ProvenanceError("role_not_allowed", source_id=source_id, role=source.role) located = locate_excerpt(source.text, excerpt) if located is not None: return VerifiedEvidence(source_id, excerpt, located[0], located[1]) for other in registry.items(): if other.source_id == source_id or excerpt not in (other.text or ""): continue if other.role not in policy.allowed_roles: raise ProvenanceError("role_not_allowed", source_id=other.source_id, role=other.role) raise ProvenanceError("wrong_source", source_id=source_id, excerpt=excerpt[:120]) raise ProvenanceError("unverified_excerpt", source_id=source_id, excerpt=excerpt[:120]) def _selected_records(registry: SourceRegistry, policy: VerificationPolicy) -> list[SourceRecord]: if policy.coverage not in COVERAGE_MODES: raise ProvenanceError("unknown_coverage", coverage=policy.coverage) selected = tuple(item for item in policy.selected_ids if item) if not selected: raise ProvenanceError("selection_empty") if len(set(selected)) != len(selected): raise ProvenanceError("duplicate_source_id", ids=list(selected)) records: list[SourceRecord] = [] for source_id in selected: source = registry.get(source_id) if source is None: raise ProvenanceError("unknown_source", source_id=source_id) if source.role not in policy.allowed_roles: raise ProvenanceError("role_not_allowed", source_id=source_id, role=source.role) records.append(source) return records def verify( registry: SourceRegistry, *, policy: VerificationPolicy, evidence: list[EvidenceRef] | None = None, annotations: list[dict[str, Any]] | None = None, proposed_texts: Mapping[str, str] | None = None, ) -> VerifiedArtifact: """Materialize a VerifiedArtifact from local sources plus untrusted references.""" selected = _selected_records(registry, policy) for source in selected: proposed = (proposed_texts or {}).get(source.source_id) if proposed is not None and proposed != source.text: raise ProvenanceError("canonical_overwrite", source_id=source.source_id) verified: list[VerifiedEvidence] = [] for ref in evidence or []: verified.append(verify_evidence(registry, ref, policy)) if policy.coverage == COVERAGE_ALL_SELECTED_SOURCES: sources = tuple( {"source_id": source.source_id, "role": source.role, "text": source.text} for source in selected ) else: grouped: dict[str, list[str]] = {source.source_id: [] for source in selected} for item in verified: grouped[item.source_id].append(item.excerpt) sources = tuple( { "source_id": source.source_id, "role": source.role, "text": "\n".join(grouped[source.source_id]), } for source in selected ) unverified: tuple[dict[str, Any], ...] = () if policy.include_unverified_annotations and annotations: unverified = tuple(dict(item) for item in annotations) encounter = tuple(item for item in policy.source_order if item) or tuple( source.source_id for source in selected ) return VerifiedArtifact( coverage=policy.coverage, source_order=encounter, sources=sources, evidence=tuple(verified), annotations_unverified=unverified, )