"""Build the report-facing projection of birth-time sensitivity evidence.""" from __future__ import annotations from copy import deepcopy from hashlib import sha256 import json from typing import Any, Mapping try: from flexible_birth_time_profile import _candidate_window_authority_violation except ImportError: # pragma: no cover - package import from scripts.flexible_birth_time_profile import _candidate_window_authority_violation SCHEMA_VERSION = "jyotish.flexible_birth_time_full_report_projection.v1" SUPPORT_SCHEMA_VERSION = "jyotish.flexible_birth_time_report_support.v1" class FlexibleBirthTimeFullReportProjectionError(ValueError): """Raised when report support cannot be projected safely.""" def build_flexible_birth_time_full_report_projection(support: Mapping[str, Any]) -> dict[str, Any]: packet = _validate_support(support) window = deepcopy(dict(packet.get("birth_time_window") or {})) return { "schema_version": SCHEMA_VERSION, "projection_id": _digest_id(str(packet.get("support_id")), json.dumps(window, sort_keys=True, separators=(",", ":"))), "report_section_type": "birth_time_sensitivity", "window": window, "stable_structure_section": deepcopy(dict(packet.get("stable_report_evidence") or {})), "minute_sensitive_section": deepcopy(dict(packet.get("sensitive_report_evidence") or {})), "theme_sensitivity": deepcopy(dict(packet.get("theme_sensitivity") or {})), "trace": deepcopy(list(packet.get("trace") or [])), "status": "candidate_window_only", "claim_boundary": str(packet.get("claim_boundary") or "Sensitivity remains conditional."), } def _validate_support(value: Mapping[str, Any]) -> dict[str, Any]: violation = _candidate_window_authority_violation(value) if violation: raise FlexibleBirthTimeFullReportProjectionError(f"candidate_window_authority_forbidden:{violation}") if not isinstance(value, Mapping) or value.get("schema_version") != SUPPORT_SCHEMA_VERSION: raise FlexibleBirthTimeFullReportProjectionError("flexible_birth_time_report_support_schema_invalid") if value.get("status") != "candidate_window_only": raise FlexibleBirthTimeFullReportProjectionError("support_must_remain_candidate_window_only") return dict(value) def _digest_id(*parts: str) -> str: payload = json.dumps(parts, ensure_ascii=True, separators=(",", ":")) return f"flex-report-projection://{sha256(payload.encode('utf-8')).hexdigest()[:24]}"