from __future__ import annotations

import base64
import logging
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, Iterable, List, Literal, Mapping, Optional, Sequence, Set

from pydantic import AliasChoices

from receipts.compat.pyd import BaseModel, ConfigDictLike, Field, field_validator, model_validator

from receipts.base import (
    BaseReceipt,
    ComplianceAssertionProof,
    ComplianceAssertionProofStep,
    Domain,
    OVERRIDE_JUSTIFICATION_DENYLIST,
    PolicyWitness,
    ReceiptAnchor,
)
from receipts.proofkit_attestation import generate_attestation

logger = logging.getLogger(__name__)


class GovernanceReceipt(BaseReceipt):
    """Base class for governance receipts."""

    domain: str = Field(default=Domain.GOVERNANCE.value)

    # Profile and receipt mode for governance compliance
    profile: str = Field(default="governance", description="Profile determining signing behavior")
    receipt_mode: str = Field(default="signed", description="Receipt mode: 'signed' or 'minimal'")
    retention_days: Optional[int] = Field(default=None, description="Optional retention period in days")
    encryption_required: bool = Field(default=True, description="Whether encryption is required for sensitive data")
    attestation: Dict[str, Any] = Field(
        default_factory=dict,
        description="ProofKit attestation bundle if the receipt is signed.",
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Ensure new fields are present explicitly
        data["profile"] = self.profile
        data["receipt_mode"] = self.receipt_mode
        data["retention_days"] = self.retention_days
        data["encryption_required"] = self.encryption_required
        data["attestation"] = self.attestation
        return data

    def sign(self, sign_key: Optional[bytes] = None, algorithm: str = "hmac-sha256") -> None:
        """Attach a ProofKit attestation when signing is enabled."""
        attestation = generate_attestation(
            self,
            key=sign_key,
            algorithm=algorithm,
            receipt_mode=self.receipt_mode,
            log=logger,
        )
        if attestation:
            self.attestation = attestation

    @model_validator(mode="after")
    def _auto_sign(self) -> "GovernanceReceipt":
        if self.attestation:
            return self
        if getattr(self, "receipt_mode", "signed") != "signed":
            return self
        attestation = generate_attestation(self, key=None, log=logger)
        if attestation:
            self.attestation = attestation
        return self


class PolicyUpdateReceipt(GovernanceReceipt):
    """Receipt emitted when a policy rule is updated (shadow or applied)."""

    receipt_type: str = "PolicyUpdateReceipt"
    organ: str = Field(default="dypee")

    rule_id: str = Field(description="Identifier of the policy rule being updated.")
    old_value: Optional[Any] = Field(
        default=None, description="Previous value of the rule (if any)."
    )
    new_value: Any = Field(description="New value proposed or applied.")
    effective_ts: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="Timestamp when the update was generated.",
    )
    confidence: float = Field(
        ge=0.0,
        le=1.0,
        description="Learner-assigned confidence in the new value.",
    )
    source_receipt_ids: List[str] = Field(
        default_factory=list,
        description="Receipts that motivated this update (e.g., Glimpse/Reasoning).",
    )
    reward_signal: float = Field(
        description="Scalar reward computed from recent receipts (learner-specific)."
    )
    meta: Dict[str, Any] = Field(
        default_factory=dict,
        description="Auxiliary metadata (e.g., applied/shadow_mode flags).",
    )


class TimestampAnomaly(str, Enum):
    MISSING_VALID_TIME = "MISSING_VALID_TIME"
    EXCESSIVE_LAG = "EXCESSIVE_LAG"
    EXCESSIVE_LEAD = "EXCESSIVE_LEAD"


class TimestampPolicyReceipt(GovernanceReceipt):
    """Policy receipt emitted when timestamp hygiene violations are detected."""

    receipt_type: str = "TimestampPolicyReceipt"
    target_receipt_id: str = Field(description="Identifier of the receipt that violated timestamp policy.")
    target_receipt_type: str = Field(description="Type of the receipt that violated timestamp policy.")
    target_domain: str = Field(description="Domain of the receipt that violated timestamp policy.")
    anomaly: TimestampAnomaly = Field(description="Kind of timestamp violation that occurred.")
    observed_valid_time: Optional[datetime] = Field(
        default=None,
        description="Original valid_time observed on the offending receipt, if present.",
    )
    observed_system_time: datetime = Field(
        description="System time assigned at write when the policy violation was detected.",
    )
    skew_seconds: Optional[float] = Field(
        default=None,
        description="Signed difference in seconds between system_time and valid_time.",
    )
    policy_threshold_seconds: Optional[float] = Field(
        default=None,
        description="Threshold used by the policy when evaluating the skew condition.",
    )
    notes: Optional[str] = Field(
        default=None,
        description="Human-readable context summarising the policy decision.",
    )


class AnchoringBatchWindow(BaseModel):
    """Time window describing the anchoring spooler execution."""

    start: datetime = Field(description="UTC timestamp when the spooler window opened.")
    end: datetime = Field(description="UTC timestamp when the spooler window closed.")

    model_config = ConfigDictLike(frozen=True)

    @model_validator(mode="after")
    def _validate_bounds(self) -> "AnchoringBatchWindow":
        if self.end < self.start:
            raise ValueError("window.end must be greater than or equal to window.start")
        return self


class AnchoringBatchCounts(BaseModel):
    """Structured counters captured during an anchoring spooler run."""

    total: int = Field(ge=0, description="Total receipts inspected during the batch window.")
    anchored: int = Field(ge=0, description="Receipts newly anchored during the batch window.")
    already_anchored: int = Field(
        ge=0,
        description="Receipts that already carried anchor metadata and were skipped.",
    )
    pending: int = Field(
        ge=0,
        description="Receipts still lacking anchors at the end of the batch window (dry-run or failures).",
    )
    failed: int = Field(ge=0, description="Receipts that failed to anchor due to errors.")
    skipped: int = Field(
        default=0,
        ge=0,
        description="Receipts ignored by the spooler (non-critical types, corrupt payloads, etc.).",
    )
    rekor_submissions: int = Field(
        default=0,
        ge=0,
        description="Number of anchors submitted to Rekor during the batch window.",
    )

    model_config = ConfigDictLike(frozen=True)

    @model_validator(mode="after")
    def _validate_totals(self) -> "AnchoringBatchCounts":
        accounted = self.anchored + self.already_anchored + self.pending + self.failed + self.skipped
        if accounted > self.total:
            raise ValueError("counts.total must be ≥ sum of anchored, already_anchored, pending, failed, and skipped")
        return self


class AnchoringBatchReceipt(GovernanceReceipt):
    """Operational receipt emitted after the anchoring spooler processes the backlog."""

    receipt_type: str = "AnchoringBatchReceipt"
    domain: str = Field(default="observability")
    mode: str = Field(default="OPS")
    window: AnchoringBatchWindow = Field(description="Time window summarising the spooler execution.")
    counts: AnchoringBatchCounts = Field(description="Structured counters for the spooler batch.")
    backlog_minutes: float = Field(
        ge=0.0,
        description="Maximum age in minutes among receipts still missing anchors after the batch window.",
    )
    notes: List[str] = Field(
        default_factory=list,
        description="Operator-facing annotations captured during the batch (threshold breaches, retry context, etc.).",
    )


class PolicyRecommendation(str, Enum):
    APPROVE = "APPROVE"
    APPROVE_WITH_GUARDRAILS = "APPROVE_WITH_GUARDRAILS"
    REJECT = "REJECT"


class PolicyImpactProjection(BaseModel):
    coverage: Optional[float] = Field(
        default=None,
        description="Predicted proportional coverage shift attributable to the proposed policy.",
    )
    ece: Optional[float] = Field(
        default=None,
        description="Expected change in calibration error under the proposed policy.",
    )
    fairness_flags: List[str] = Field(
        default_factory=list,
        description="List of fairness guardrails projected to be impacted.",
    )


class PolicyPreflight(GovernanceReceipt):
    """Policy change preflight receipt summarising predicted downstream impacts."""

    model_config = ConfigDictLike(
        json_schema_extra={
            "examples": [
                {
                    "receipt_id": "policy_preflight_gatebook@2.3_proposal",
                    "domain": "mlb_engine",
                    "policy_diff": {"gate": "dwf_floor", "from": 0.72, "to": 0.74},
                    "simulation_window": "2025-10-23/2025-10-29",
                    "predicted_impacts": {
                        "coverage": 0.02,
                        "ece": 0.004,
                        "fairness_flags": ["cohort_fairness_warning"],
                    },
                    "recommendation": "APPROVE_WITH_GUARDRAILS",
                    "affected_receipts": 128,
                    "divergent_decisions": 11,
                }
            ]
        }
    )

    receipt_type: str = "PolicyPreflight"
    policy_diff: Mapping[str, Any] = Field(
        description="Structured diff describing the proposed policy or gatebook change."
    )
    simulation_window: str = Field(
        description="Inclusive window used for replay or simulation, expressed as ISO-8601 interval."
    )
    predicted_impacts: PolicyImpactProjection = Field(
        description="Projected impacts covering coverage, calibration, and fairness signals."
    )
    recommendation: PolicyRecommendation = Field(
        description="Recommended course of action derived from the preflight analysis."
    )
    affected_receipts: int = Field(
        description="Number of downstream receipts that would be impacted by the proposed policy."
    )
    divergent_decisions: int = Field(
        description="Count of decisions that diverge from the baseline when applying the proposal."
    )

    @classmethod
    def validate(cls, data: Mapping[str, Any]) -> "PolicyPreflight":
        if isinstance(data, cls):
            return data
        return cls.model_validate(data)


class PolicyFreezeProposal(GovernanceReceipt):
    """Governance receipt proposing a policy freeze when drift risk exceeds thresholds."""

    receipt_type: str = "PolicyFreezeProposal"
    risk_score: float = Field(
        description="Computed constitutional drift risk score for the evaluation window.",
    )
    freeze_threshold: float = Field(
        description="Gatebook-configured risk threshold that triggers a freeze proposal.",
    )
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Metric values at the time of evaluation keyed by identifier.",
    )
    baselines: Dict[str, Any] = Field(
        default_factory=dict,
        description="Baseline metric values used for comparison (e.g., 30d aggregates).",
    )
    violations: List[str] = Field(
        default_factory=list,
        description="Metric identifiers whose drift breached configured thresholds.",
    )
    rationale: Optional[str] = Field(
        default=None,
        description="Human-readable summary describing why the freeze is recommended.",
    )
    policy_bundle_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the active policy bundle at evaluation time.",
    )
    gatebook_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the active gatebook at evaluation time.",
    )
    snapshot_receipt_id: Optional[str] = Field(
        default=None,
        description="PulseSnapshot receipt identifier that triggered the freeze proposal.",
    )
    freeze_scope: Literal["policy", "gatebook", "autonomy"] = Field(
        default="policy",
        description="Scope of the proposed freeze (policy, gatebook, or autonomy).",
    )
    anchor: Optional[ReceiptAnchor] = Field(
        default=None,
        description="Sigstore Rekor anchoring metadata for the freeze proposal.",
    )


class PolicyUnfreezeProposal(GovernanceReceipt):
    """Governance receipt proposing to lift an active policy freeze after remediation."""

    receipt_type: str = "PolicyUnfreezeProposal"
    freeze_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier of the originating PolicyFreezeProposal.",
    )
    proof_receipt_id: Optional[str] = Field(
        default=None,
        description="ProofOfRepairReceipt identifier motivating the unfreeze proposal.",
    )
    decision: Literal["PROPOSE_UNFREEZE", "DEFER"] = Field(
        description="Automated proposal verdict (propose unfreeze or defer).",
    )
    reasons: List[str] = Field(
        default_factory=list,
        description="Human-readable reasons supporting the proposal outcome.",
    )
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Key metric deltas and confidence intervals captured at proposal time.",
    )
    snapshot_refs: List[str] = Field(
        default_factory=list,
        description="PulseSnapshot or evaluation receipt identifiers backing the proposal.",
    )
    remediation_refs: List[str] = Field(
        default_factory=list,
        description="Remediation-related receipt identifiers referenced by the proposal.",
    )
    policy_bundle_hash: Optional[str] = Field(
        default=None,
        description="SHA-256 digest of the active policy bundle when the proposal was generated.",
    )
    policy_version: Optional[str] = Field(
        default=None,
        description="Policy bundle semantic version when the proposal was generated.",
    )
    gatebook_hash: Optional[str] = Field(
        default=None,
        description="Gatebook hash corresponding to the frozen configuration.",
    )
    policy_bundle_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the policy bundle associated with the freeze.",
    )
    gatebook_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the gatebook associated with the freeze.",
    )
    anchor: Optional[ReceiptAnchor] = Field(
        default=None,
        description="Optional transparency anchor metadata for the unfreeze proposal.",
    )


class FreezeDecisionReceipt(GovernanceReceipt):
    """Final governance decision documenting freeze state after assessing remediation."""

    receipt_type: str = "FreezeDecisionReceipt"
    freeze_receipt_id: Optional[str] = Field(
        default=None,
        description="Reference to the PolicyFreezeProposal governing this decision.",
    )
    unfreeze_proposal_id: Optional[str] = Field(
        default=None,
        description="PolicyUnfreezeProposal identifier considered for the decision.",
    )
    proof_receipt_id: Optional[str] = Field(
        default=None,
        description="ProofOfRepairReceipt identifier backing the decision.",
    )
    decision: Literal["FREEZE_LIFTED", "FREEZE_MAINTAINED"] = Field(
        description="Final freeze state after evaluation.",
    )
    reasons: List[str] = Field(
        default_factory=list,
        description="Narrative justifications for the decision outcome.",
    )
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Metric summaries and deltas used when making the decision.",
    )
    snapshot_refs: List[str] = Field(
        default_factory=list,
        description="PulseSnapshot or evaluation receipts that contributed to the decision.",
    )
    remediation_refs: List[str] = Field(
        default_factory=list,
        description="Remediation receipts referenced by the decision.",
    )
    policy_bundle_hash: Optional[str] = Field(
        default=None,
        description="SHA-256 digest of the policy bundle at decision time.",
    )

    policy_version: Optional[str] = Field(
        default=None,
        description="Policy bundle version at decision time.",
    )


class GuardianVerdictDecision(str, Enum):
    APPROVE = "APPROVE"
    REJECT = "REJECT"
    DEGRADE = "DEGRADE"


class GuardianVerdictReceipt(GovernanceReceipt):
    """Receipt emitted when a Guardian policy determines a decision for a request.

    Use this to capture the outcome of OPA or other policy evaluations that may
    allow, reject, or downgrade a decision. This is intentionally small — it
    points to the authoritative replay passport and includes the evidence used
    and simple reasons for human review.
    """

    receipt_type: str = "GuardianVerdictReceipt"
    passport_id: Optional[str] = Field(default=None, description="Passport id evaluated")
    decision: GuardianVerdictDecision = Field(description="Decision issued by guardian")
    reasons: List[str] = Field(default_factory=list, description="Human-readable reasons for the decision")
    policy_bundle_receipt_id: Optional[str] = Field(default=None, description="policy bundle used for decision")
    evidence: Dict[str, Any] = Field(default_factory=dict, description="Structured evidence passed to policy")
    gatebook_hash: Optional[str] = Field(
        default=None,
        description="Gatebook hash at decision time.",
    )
    policy_bundle_receipt_id: Optional[str] = Field(
        default=None,
        description="Policy bundle receipt identifier attached to the decision record.",
    )
    gatebook_receipt_id: Optional[str] = Field(
        default=None,
        description="Gatebook receipt identifier attached to the decision record.",
    )


class ConstitutionalSnapshot(GovernanceReceipt):
    """Daily constitutional health snapshot summarizing guardrail posture."""

    model_config = ConfigDictLike(
        json_schema_extra={
            "examples": [
                {
                    "receipt_id": "constitutional_snapshot_2025-10-30",
                    "domain": "governance",
                    "snapshot_ts": "2025-10-30T00:00:00Z",
                    "active_policies": ["gatebook@2.3", "persona@sportsbook_ops_v1"],
                    "policy_merkle_root": "sha256:abc123",
                    "cumulative_risk_score": 0.18,
                    "dignity_floor_skip_rate": 0.0,
                    "market_dependency_lambda": 0.62,
                    "refusal_rate": 0.11,
                    "human_override_rate": 0.02,
                    "cohort_entropy": 2.41,
                    "risk_velocity": 0.03,
                    "policy_churn": 3,
                }
            ]
        }
    )

    receipt_type: str = "ConstitutionalSnapshot"
    snapshot_ts: datetime
    active_policies: List[str] = Field(default_factory=list)
    policy_merkle_root: str
    cumulative_risk_score: float
    dignity_floor_skip_rate: float
    market_dependency_lambda: float
    refusal_rate: float
    human_override_rate: float
    cohort_entropy: float
    risk_velocity: float
    policy_churn: int
    audit_completeness: Optional[float] = Field(default=None)
    fairness_coverage: Optional[float] = Field(default=None)

    @classmethod
    def validate(cls, data: Mapping[str, Any]) -> "ConstitutionalSnapshot":
        if isinstance(data, cls):
            return data
        return cls.model_validate(data)


class DriftType(str, Enum):
    POLICY_CREEP = "policy_creep"
    MONOCULTURE = "monoculture"
    ALIGNMENT_GAP = "alignment_gap"
    DIGNITY_EROSION = "dignity_erosion"
    MARKET_CAPTURE = "market_capture"


class DriftAlert(GovernanceReceipt):
    """Alert emitted when constitutional health breaches risk thresholds."""

    model_config = ConfigDictLike(
        json_schema_extra={
            "examples": [
                {
                    "receipt_id": "drift_alert_2025-10-30T00:00:00Z",
                    "domain": "governance",
                    "trigger_snapshot_id": "constitutional_snapshot_2025-10-30",
                    "drift_type": "dignity_erosion",
                    "risk_level": 0.92,
                    "contributing_changes": ["PR#182-gatebook@2.3", "policy_change#77"],
                    "recommended_rollback": "ENFORCE_DWF_GATE",
                    "human_review_required": True,
                    "auto_remediation": "ENFORCE_DWF_GATE",
                }
            ]
        }
    )

    receipt_type: str = "DriftAlert"
    trigger_snapshot_id: str
    drift_type: DriftType
    risk_level: float
    contributing_changes: List[str] = Field(default_factory=list)
    recommended_rollback: Optional[str] = None
    human_review_required: bool = False
    auto_remediation: Optional[str] = None

    @classmethod
    def validate(cls, data: Mapping[str, Any]) -> "DriftAlert":
        if isinstance(data, cls):
            return data
        return cls.model_validate(data)


class GateTuningReceipt(GovernanceReceipt):
    """Receipt documenting automated gate threshold tuning."""

    receipt_type: str = "GateTuningReceipt"
    tuning_id: str = Field(description="Identifier assigned to the tuning session.")
    algorithm: str = Field(default="grid_search", description="Optimisation algorithm used.")
    gatebook_hash: str = Field(description="SHA-256 hash of the gatebook prior to tuning.")
    tuned_thresholds: Dict[str, float] = Field(
        default_factory=dict,
        description="Winning threshold values discovered during tuning.",
    )
    objective_value: float = Field(description="Objective score for the winning configuration.")
    constraints: Dict[str, Any] = Field(
        default_factory=dict,
        description="Constraint payload enforced during tuning (e.g., ECE and coverage bands).",
    )
    evaluations: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="Trace of evaluated configurations and their outcomes.",
    )
    winning_metrics: Dict[str, float] = Field(
        default_factory=dict,
        description="Metrics associated with the winning configuration.",
    )
    search_space: Dict[str, Any] = Field(
        default_factory=dict,
        description="Bounds explored during the tuning run.",
    )
    seed: Optional[int] = Field(default=None, description="Seed used for reproducibility.")
    notes: Optional[str] = Field(default=None, description="Optional notes about the tuning run.")
    mode: str = Field(default="RESEARCH", description="Execution mode for gate tuning.")


class OverrideDisposition(str, Enum):
    APPROVE = "APPROVE"
    DENY = "DENY"
    ESCALATE = "ESCALATE"


class GuardianApprovalStatus(str, Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    EXPIRED = "expired"


class MultiSigApprovalState(str, Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED_LOCKED = "rejected_locked"


class OverrideRiskLevel(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


class GuardianSignature(BaseModel):
    guardian_id: str = Field(description="Unique identifier for the guardian")
    guardian_email: str = Field(description="Email of the guardian")
    status: GuardianApprovalStatus = Field(default=GuardianApprovalStatus.PENDING)
    signed_at: Optional[datetime] = None
    signature_hash: Optional[str] = Field(default=None, description="Cryptographic signature of approval")
    notes: Optional[str] = Field(default=None, description="Guardian's rationale or notes")
    risk_tier: Optional[OverrideRiskLevel] = Field(
        default=None,
        description="Risk tier associated with the override when signed",
    )


class OverrideQualityVerdict(str, Enum):
    ACCEPT = "ACCEPT"
    REVIEW = "REVIEW"
    REJECT = "REJECT"


class OverrideQualityReceipt(GovernanceReceipt):
    """Receipt capturing Guardian override justification quality assessment."""

    receipt_type: str = "OverrideQualityReceipt"
    approval_id: Optional[str] = Field(
        default=None,
        description="Linked MultiSigGuardianApproval receipt identifier, when available.",
    )
    policy: Dict[str, Any] = Field(
        default_factory=dict,
        description="Resolved override quality policy at evaluation time.",
    )
    score: float = Field(
        ge=0.0,
        le=1.0,
        description="Heuristic quality score, normalised to the range [0, 1].",
    )
    verdict: OverrideQualityVerdict = Field(
        default=OverrideQualityVerdict.REVIEW,
        description="Outcome of the quality gate evaluation.",
    )
    reasons: List[str] = Field(
        default_factory=list,
        description="Narrative factors contributing to the score (positives and negatives).",
    )
    justification_length: int = Field(
        ge=0,
        description="Normalised character length of the override justification.",
    )
    justification_excerpt: Optional[str] = Field(
        default=None,
        description="Truncated excerpt of the justification for audit review.",
    )

    @field_validator("reasons", mode="before")
    @classmethod
    def _normalise_reasons(cls, value: Any) -> List[str]:
        if value is None:
            return []
        if isinstance(value, str):
            token = value.strip()
            return [token] if token else []
        if isinstance(value, Iterable):
            cleaned: List[str] = []
            for entry in value:
                token = str(entry).strip()
                if token:
                    cleaned.append(token)
            return cleaned
        return []

    @model_validator(mode="after")
    def _post_init(self) -> "OverrideQualityReceipt":
        if self.justification_excerpt is None and isinstance(self.context, Mapping):
            excerpt = self.context.get("justification_excerpt")
            if isinstance(excerpt, str):
                object.__setattr__(self, "justification_excerpt", excerpt)
        return self


class OverrideReceipt(GovernanceReceipt):
    """Receipt recording manual override justifications and approvals."""

    receipt_type: str = "OverrideReceipt"
    parent_id: str = Field(
        ...,
        validation_alias=AliasChoices("parent_id", "parent_prophecy_id"),
        description="Decision receipt identifier being overridden (ProphecyStone or RefusalStone).",
    )
    override_type: OverrideDisposition = Field(
        ...,
        description="Disposition applied by the human reviewer.",
    )
    reason_codes: List[str] = Field(
        default_factory=list,
        description="Normalized override reason codes supporting structured analytics.",
    )
    free_text: str = Field(
        ...,
        validation_alias=AliasChoices("free_text", "justification"),
        description="Narrative justification provided by the human actor.",
    )
    actor_id: str = Field(
        ...,
        validation_alias=AliasChoices("actor_id", "authorized_by"),
        description="Identifier (email or staff ID) for the actor performing the override.",
    )
    actor_role: str = Field(
        default="unknown",
        description="Role of the actor (e.g., operations, guardian, clinician).",
    )
    policy_bundle_hash: Optional[str] = Field(
        default=None,
        description="Policy bundle SHA-256 hash in effect at the time of override.",
    )
    policy_bundle_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the referenced policy bundle freeze.",
    )
    gatebook_hash: Optional[str] = Field(
        default=None,
        description="Gatebook SHA-256 hash in effect at the time of override.",
    )
    gatebook_receipt_id: Optional[str] = Field(
        default=None,
        description="Receipt identifier for the referenced gatebook freeze.",
    )
    gate_overrides: Dict[str, Any] = Field(
        default_factory=dict,
        description="Legacy gate status payload retained for backward compatibility.",
    )
    override_context: Dict[str, Any] = Field(
        default_factory=dict,
        description="Structured context captured at override time (deprecated in favour of context).",
    )
    guardian_receipt_id: Optional[str] = Field(
        default=None,
        description="Linked MultiSigGuardianApproval receipt identifier, when applicable.",
    )
    signatures: List[GuardianSignature] = Field(
        default_factory=list,
        description="Guardian signature records for multi-sig overrides.",
    )
    outcome: Literal["pending", "approved", "rejected", "expired"] = Field(
        default="pending",
        description="Current lifecycle outcome for the override workflow.",
    )
    quality_score: Optional[float] = Field(
        default=None,
        ge=0.0,
        le=1.0,
        description="Override quality score captured when the override was submitted.",
    )
    quality_verdict: Optional[OverrideQualityVerdict] = Field(
        default=None,
        description="Verdict from the override quality gate.",
    )
    quality_receipt_id: Optional[str] = Field(
        default=None,
        description="OverrideQualityReceipt identifier capturing the evaluation.",
    )
    quality_reasons: List[str] = Field(
        default_factory=list,
        description="Factors captured during the override quality evaluation.",
    )

    @field_validator("override_type", mode="before")
    @classmethod
    def _normalise_override_type(cls, value: Any) -> OverrideDisposition:
        if isinstance(value, OverrideDisposition):
            return value
        if isinstance(value, str):
            token = value.strip().upper()
            legacy_map = {
                "MANUAL_PREDICTION": OverrideDisposition.APPROVE,
                "BYPASS_GATES": OverrideDisposition.ESCALATE,
                "EMERGENCY_RELEASE": OverrideDisposition.APPROVE,
            }
            mapped = legacy_map.get(token)
            if mapped is not None:
                return mapped
            try:
                return OverrideDisposition[token]
            except KeyError:
                pass
            try:
                return OverrideDisposition(token)
            except ValueError as exc:
                raise ValueError("override_type must be one of APPROVE, DENY, ESCALATE") from exc
        raise ValueError("override_type must be one of APPROVE, DENY, ESCALATE")

    @field_validator("reason_codes", mode="before")
    @classmethod
    def _prepare_reason_codes(cls, value: Any) -> List[str]:
        if value is None:
            return []
        if isinstance(value, str):
            value = [value]
        if not isinstance(value, Sequence):
            raise TypeError("reason_codes must be a sequence of strings")
        prepared: List[str] = []
        seen: Set[str] = set()
        for item in value:
            token = str(item).strip()
            if not token:
                continue
            normalised = token.replace(" ", "_").upper()
            if normalised not in seen:
                prepared.append(normalised)
                seen.add(normalised)
        return prepared

    @field_validator("free_text")
    @classmethod
    def _validate_free_text(cls, value: str) -> str:
        stripped = value.strip()
        if len(stripped) < 20:
            raise ValueError("free_text must be at least 20 characters long")
        collapsed = "".join(ch for ch in stripped.lower() if ch.isalpha())
        if collapsed in OVERRIDE_JUSTIFICATION_DENYLIST:
            raise ValueError("free_text contains boilerplate text")
        return stripped

    @field_validator("actor_id")
    @classmethod
    def _normalise_actor_id(cls, value: str) -> str:
        token = value.strip()
        if not token:
            raise ValueError("actor_id must be provided")
        return token

    @field_validator("actor_role")
    @classmethod
    def _normalise_actor_role(cls, value: str) -> str:
        token = value.strip() or "unknown"
        return token

    @model_validator(mode="after")
    def _finalise(self) -> "OverrideReceipt":
        parent_ids = list(self.parent_ids or [])
        if self.parent_id and self.parent_id not in parent_ids:
            parent_ids.insert(0, self.parent_id)
        object.__setattr__(self, "parent_ids", parent_ids)

        related = dict(self.related_ids or {})
        if self.parent_id:
            related.setdefault("decision_receipt_id", self.parent_id)
        if self.policy_bundle_receipt_id:
            related.setdefault("policy_bundle_receipt_id", self.policy_bundle_receipt_id)
        if self.gatebook_receipt_id:
            related.setdefault("gatebook_receipt_id", self.gatebook_receipt_id)
        object.__setattr__(self, "related_ids", related)

        context_block = dict(self.context or {})
        override_block = dict(self.override_context or {})
        if context_block and not override_block:
            override_block = dict(context_block)
        elif override_block and not context_block:
            context_block = dict(override_block)
        else:
            merged = dict(override_block)
            merged.update(context_block)
            context_block = merged
            override_block = merged
        object.__setattr__(self, "context", context_block)
        object.__setattr__(self, "override_context", override_block)
        return self

    def is_approved(self) -> bool:
        return self.outcome == "approved"


class VetoQueueReceipt(GovernanceReceipt):
    """
    Receipt emitted when Guardian ESCALATE decision triggers veto queue entry (Phase 1C.1).
    
    Constitutional spine: When Guardian returns ESCALATE (contradictions.rego or proof_tier.rego
    requires human review), this receipt captures the decision for governance review.
    
    Semantics (SEMANTICS_RUNBOOK.md):
    - Structure: receipt (signed, replayable governance record)
    - Function: queue_entry (enqueues decision for human/policy review)
    - Meaning: "This decision was escalated for constitutional review due to behavioral
      contradictions, dignity incidents, or proof tier elevation requirements"
    - Dimensions:
      - Time: valid_time = decision moment, system_time = queue insertion
      - Scope: tenant-scoped (governance domain)
      - Authority: Guardian (canonical policy enforcement)
      - Risk: high (requires human oversight before finalization)
    """

    receipt_type: str = "VetoQueueReceipt"
    
    # Core identifiers
    decision_id: str = Field(description="Unique decision identifier (links to EpistemicSnapshotReceipt)")
    snapshot_id: str = Field(description="EpistemicSnapshotReceipt identifier for this decision")
    proof_tier_id: str = Field(description="ProofTierReceipt identifier for Guardian verdict")
    
    # Escalation context
    reason: str = Field(
        description="Primary escalation reason: 'contradictions', 'proof_tier_elevation', 'dignity_floor_proximity'"
    )
    severity: Literal["low", "medium", "high", "critical"] = Field(
        default="medium",
        description="Escalation severity (from Guardian contradictions.rego or proof_tier.rego)",
    )
    escalate_reasons: List[str] = Field(
        default_factory=list,
        description="Detailed escalation reasons from Guardian verdict (contradictions + proof tier)",
    )
    
    # Guardian verdict summary
    guardian_decision: Literal["ESCALATE"] = Field(
        default="ESCALATE",
        description="Guardian decision that triggered veto queue (always ESCALATE for this receipt)",
    )
    require_tier: Literal["core", "regulated", "court"] = Field(
        default="regulated",
        description="Required proof tier from Guardian (core/regulated/court)",
    )
    route: str = Field(
        default="consensus.hold",
        description="Guardian route (e.g., consensus.hold, consensus.human_review)",
    )
    
    # Contradictions summary (from contradictions.rego)
    contradictions_summary: Dict[str, Any] = Field(
        default_factory=dict,
        description="Summary of behavioral contradictions: risk_level, contradiction_type, deny/escalate/warn flags",
    )
    
    # Metadata
    domain: str = Field(
        default="governance",
        description="Domain where decision originated (apm, clinical, mlb, etc.)",
    )
    asset_id: Optional[str] = Field(
        default=None,
        description="Asset identifier (payment_id, patient_id, game_id, etc.) if applicable",
    )
    
    # Semantics block (SEMANTICS_RUNBOOK.md compliant)
    semantics: Dict[str, Any] = Field(
        default_factory=lambda: {
            "structural_type": "receipt",
            "functional_role": "queue_entry",
            "meaning_tags": ["escalation", "veto_queue", "human_review", "guardian_escalate"],
            "scope": "tenant",
            "authority": "guardian",
            "risk_level": "high",
        },
        description="Semantic metadata: structure/function/meaning per SEMANTICS_RUNBOOK.md",
    )


class DissentReceipt(GovernanceReceipt):
    """
    Receipt documenting formal dissent against a prior decision (Phase 1C.1).
    
    Constitutional dissent: Any authorized actor (operator, guardian, agent) can emit
    a DissentReceipt to formally disagree with a completed decision, triggering review.
    
    Semantics (SEMANTICS_RUNBOOK.md):
    - Structure: receipt (signed, replayable governance record)
    - Function: dissent_marker (flags decision for review/audit)
    - Meaning: "I formally disagree with this decision and request review"
    - Dimensions:
      - Time: valid_time = dissent moment, links back to original decision
      - Scope: tenant-scoped (governance domain)
      - Authority: varies (operator, guardian, agent - captured in 'actor' field)
      - Risk: medium-high (triggers governance review workflow)
    """

    receipt_type: str = "DissentReceipt"
    
    # Target decision
    target_decision_id: str = Field(description="Decision being contested (links to original receipt)")
    target_receipt_type: str = Field(
        description="Type of receipt being contested (PaymentReceipt, ProphecyStone, etc.)"
    )
    
    # Dissent details
    reason: str = Field(
        min_length=20,
        description="Justification for dissent (minimum 20 characters, detailed explanation required)",
    )
    actor: str = Field(description="Actor lodging dissent (operator_id, guardian_id, agent_id)")
    actor_role: Literal["operator", "guardian", "agent", "reviewer"] = Field(
        default="operator",
        description="Role of dissenting actor",
    )
    
    # Context
    severity: Literal["low", "medium", "high", "critical"] = Field(
        default="medium",
        description="Severity of disagreement",
    )
    domain: str = Field(
        default="governance",
        description="Domain of contested decision (apm, clinical, mlb, etc.)",
    )
    
    # Review triggers
    review_requested: bool = Field(
        default=True,
        description="Whether dissent should trigger formal review process",
    )
    escalate_to_court: bool = Field(
        default=False,
        description="Whether dissent severity requires COURT tier escalation",
    )
    
    # Semantics block (SEMANTICS_RUNBOOK.md compliant)
    semantics: Dict[str, Any] = Field(
        default_factory=lambda: {
            "structural_type": "receipt",
            "functional_role": "dissent_marker",
            "meaning_tags": ["dissent", "review_trigger", "governance_challenge"],
            "scope": "tenant",
            "authority": "varies",
            "risk_level": "medium_high",
        },
        description="Semantic metadata: structure/function/meaning per SEMANTICS_RUNBOOK.md",
    )


class VetoDecisionReceipt(GovernanceReceipt):
    """Receipt documenting an operator veto against a model decision."""

    receipt_type: str = "VetoDecisionReceipt"
    asset_id: str = Field(description="Asset identifier shared with the underlying prophecy.")
    parent_prophecy_id: str = Field(
        validation_alias=AliasChoices("parent_prophecy_id", "parent_id"),
        description="ProphecyStone being vetoed.",
    )
    veto_reason: str = Field(description="Justification provided by the vetoing operator.")
    reviewer: str = Field(description="Operator or team applying the veto.")
    policy_witness: Optional[PolicyWitness] = Field(
        default=None,
        description="Embedded policy witness snapshot for auditing.",
    )


class MultiSigGuardianApproval(GovernanceReceipt):
    """Multi-signature guardian approval for high-risk gate overrides."""

    receipt_type: str = "MultiSigGuardianApproval"
    asset_id: str = Field(description="Asset being overridden")
    parent_prophecy_id: Optional[str] = Field(default=None, description="Linked prophecy if issued")
    override_type: str = Field(description="Type of override: 'worst_slice_ece', 'evhd_monotonicity', etc.")
    risk_level: OverrideRiskLevel = Field(description="Risk classification")
    gate_overrides: Dict[str, Any] = Field(
        default_factory=dict,
        description="Specific gate values being overridden",
    )
    override_justification: str = Field(
        min_length=50,
        description="Detailed justification for override (minimum 50 characters)",
    )
    override_actor: str = Field(description="Original actor requesting the override")
    n_required: int = Field(ge=1, description="Number of approvals required")
    m_total: int = Field(ge=1, description="Total number of guardians")
    guardian_pool: List[str] = Field(default_factory=list, description="List of authorized guardian IDs")
    signatures: List[GuardianSignature] = Field(default_factory=list)
    approval_deadline: Optional[datetime] = None
    approved_at: Optional[datetime] = None
    rejected_at: Optional[datetime] = None
    freeze_on_rejection: bool = Field(default=True, description="Lock quorum once any guardian rejects")
    slack_thread_ts: Optional[str] = Field(default=None, description="Slack thread timestamp for escalation")
    slack_channel: Optional[str] = Field(default=None, description="Slack channel for escalation")
    escalation_sent_at: Optional[datetime] = None
    decision_history: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="Chronological log of approval/rejection events",
    )
    override_quality_score: Optional[float] = Field(
        default=None,
        ge=0.0,
        le=1.0,
        description="Override quality score captured during submission.",
    )
    override_quality_verdict: Optional[OverrideQualityVerdict] = Field(
        default=None,
        description="Verdict recorded by OverrideQualityReceipt.",
    )
    override_quality_receipt_id: Optional[str] = Field(
        default=None,
        description="Linked OverrideQualityReceipt identifier.",
    )
    override_quality_reasons: List[str] = Field(
        default_factory=list,
        description="Factors contributing to the recorded quality score.",
    )

    @model_validator(mode="after")
    def _validate_quorum_config(self) -> "MultiSigGuardianApproval":
        unique_pool = set(self.guardian_pool)
        if len(unique_pool) != len(self.guardian_pool):
            raise ValueError("guardian_pool cannot contain duplicate guardian IDs")
        if self.n_required < 1:
            raise ValueError("n_required must be >= 1")
        if self.n_required > len(unique_pool):
            raise ValueError("n_required cannot exceed guardian_pool size")
        if self.m_total < self.n_required:
            raise ValueError("m_total must be >= n_required")
        if self.m_total < len(unique_pool):
            raise ValueError("m_total cannot be less than guardian_pool size")
        return self

    def _guardian_pool_set(self) -> Set[str]:
        return set(self.guardian_pool)

    def _unique_approvals(self) -> Set[str]:
        return {
            sig.guardian_id
            for sig in self.signatures
            if sig.status == GuardianApprovalStatus.APPROVED
        }

    def _has_rejection(self) -> bool:
        return any(sig.status == GuardianApprovalStatus.REJECTED for sig in self.signatures)

    @property
    def status(self) -> MultiSigApprovalState:
        if self.freeze_on_rejection and self._has_rejection():
            return MultiSigApprovalState.REJECTED_LOCKED
        if len(self._unique_approvals()) >= self.n_required:
            return MultiSigApprovalState.APPROVED
        return MultiSigApprovalState.PENDING

    def is_approved(self) -> bool:
        return self.status == MultiSigApprovalState.APPROVED

    def is_rejected(self) -> bool:
        return self.status == MultiSigApprovalState.REJECTED_LOCKED

    def is_expired(self) -> bool:
        if self.approval_deadline is None:
            return False
        return datetime.now(timezone.utc) > self.approval_deadline

    def add_signature(
        self,
        guardian_id: str,
        guardian_email: str,
        approved: bool,
        notes: Optional[str] = None,
        signature_hash: Optional[str] = None,
    ) -> None:
        pool = self._guardian_pool_set()
        if guardian_id not in pool:
            raise PermissionError(f"guardian_id '{guardian_id}' is not authorized")
        if self.freeze_on_rejection and self._has_rejection():
            raise ValueError("cannot modify signatures: approval locked by rejection")

        status = GuardianApprovalStatus.APPROVED if approved else GuardianApprovalStatus.REJECTED
        for existing in self.signatures:
            if existing.guardian_id == guardian_id:
                raise ValueError(f"guardian '{guardian_id}' has already recorded a decision")

        sig = GuardianSignature(
            guardian_id=guardian_id,
            guardian_email=guardian_email,
            status=status,
            signed_at=datetime.now(timezone.utc),
            signature_hash=signature_hash,
            notes=notes,
            risk_tier=self.risk_level,
        )
        self.signatures.append(sig)

        self.decision_history.append(
            {
                "ts": datetime.now(timezone.utc).isoformat(),
                "guardian_id": guardian_id,
                "action": "approve" if approved else "reject",
                "notes": notes,
            }
        )

        if self.status == MultiSigApprovalState.APPROVED and not self.approved_at:
            object.__setattr__(self, "approved_at", datetime.now(timezone.utc))
        elif status == GuardianApprovalStatus.REJECTED and not self.rejected_at:
            object.__setattr__(self, "rejected_at", datetime.now(timezone.utc))


class PolicyChangeReceipt(GovernanceReceipt):
    """Receipt documenting policy-affecting changes performed via control CLIs."""

    receipt_type: str = "PolicyChangeReceipt"
    policy_kind: str
    changed_by: str
    before: Dict[str, Any] = Field(default_factory=dict)
    after: Dict[str, Any] = Field(default_factory=dict)
    reason: Optional[str] = None
    approval_status: Optional[Literal["proposed", "approved", "rejected", "hold", "pending"]] = None

    @property
    def created_at(self) -> datetime:
        return self.ts


class GatebookChangeReceipt(GovernanceReceipt):
    """Receipt emitted when the active gatebook is reloaded at runtime."""

    receipt_type: str = "GatebookChangeReceipt"
    actor: str = Field(description="Principal that initiated the reload event")
    policy_digest: str = Field(description="SHA-256 digest for the newly applied gatebook payload")
    previous_digest: Optional[str] = Field(
        default=None,
        description="Digest of the previously active gatebook payload",
    )
    gatebook_version: Optional[str] = Field(
        default=None,
        description="Semantic version token declared by the gatebook document",
    )
    gatebook_path: str = Field(description="Filesystem location or logical path of the gatebook source")
    changes: Dict[str, Dict[str, Any]] = Field(
        default_factory=dict,
        description="Minimal diff for changed thresholds keyed by dotted path containing old/new values",
    )


class OptimizationGuardReceipt(GovernanceReceipt):
    """Receipt proving that a proposed gatebook optimization satisfies constraints."""

    receipt_type: str = "OptimizationGuardReceipt"
    actor: str
    window_days: int
    objective: str = Field(default="multi_objective_scalarization")
    weights: Dict[str, float] = Field(default_factory=dict)
    constraints: Dict[str, Any] = Field(default_factory=dict)
    satisfied: bool = False
    baseline: Dict[str, Any] = Field(default_factory=dict)
    candidate: Dict[str, Any] = Field(default_factory=dict)
    search_space: Dict[str, Any] = Field(default_factory=dict)
    diffs: Dict[str, Any] = Field(default_factory=dict)
    failure_slices: List[Dict[str, Any]] = Field(default_factory=list)
    gatebook_path: Optional[str] = None

    @property
    def created_at(self) -> datetime:
        return self.ts


class EvidenceQuorumReceipt(GovernanceReceipt):
    """Receipt emitted when evidence release quorum transitions to granted."""

    receipt_type: str = "EvidenceQuorumReceipt"
    artifact_id: str
    policy_window_days: int
    quorum_state: Literal["pending", "granted", "revoked"]
    required_quorum: int = 2
    approvers: List[Dict[str, Any]] = Field(default_factory=list)

    @property
    def created_at(self) -> datetime:
        return self.ts


class ComplianceAssertionReceipt(GovernanceReceipt):
    """Auditor-scoped attestation that a set of receipts satisfied mapped controls."""

    receipt_type: str = "ComplianceAssertionReceipt"
    assertion_id: str = Field(description="Stable identifier for the compliance assertion.")
    covered_receipt_ids: List[str] = Field(
        default_factory=list,
        description="Receipt identifiers included in the compliance assertion.",
    )
    merkle_root_b64: str = Field(description="Base64-encoded Merkle root over covered receipt hashes.")
    control_map: Dict[str, Any] = Field(
        default_factory=dict,
        description="Mapping of control identifiers to evidence metadata.",
    )
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Aggregated metrics supporting the assertion (e.g., counts, coverage).",
    )
    inclusion_proofs: Dict[str, ComplianceAssertionProof] = Field(
        default_factory=dict,
        description="Merkle inclusion proofs keyed by receipt_id.",
    )
    anchor: Optional[ReceiptAnchor] = Field(
        default=None,
        description="Optional transparency anchor tying the assertion to an external log.",
    )

    @field_validator("merkle_root_b64")
    @classmethod
    def _validate_root(cls, value: str) -> str:
        try:
            decoded = base64.b64decode(value, validate=True)
        except (ValueError, TypeError) as exc:
            raise ValueError("merkle_root_b64 must be valid base64") from exc
        if len(decoded) != 32:
            raise ValueError("merkle_root_b64 must decode to 32 bytes")
        return value

    @model_validator(mode="after")
    def _ensure_proofs_cover_all(self) -> "ComplianceAssertionReceipt":
        missing = set(self.covered_receipt_ids) - set(self.inclusion_proofs.keys())
        if missing:
            raise ValueError(f"Missing inclusion proofs for receipt ids: {sorted(missing)}")
        return self


class QuorumSatisfiedReceipt(GovernanceReceipt):
    """Generic receipt emitted when a quorum requirement is satisfied."""

    receipt_type: str = "QuorumSatisfiedReceipt"
    artifact_id: str
    policy_window_days: int
    required_quorum: int
    approvers: List[Dict[str, Any]] = Field(default_factory=list)

    @property
    def created_at(self) -> datetime:
        return self.ts


class OperationsDecisionReceipt(GovernanceReceipt):
    """Receipt emitted when operations triage a Pulse anomaly."""

    model_config = ConfigDictLike(
        json_schema_extra={
            "examples": [
                {
                    "receipt_id": "ops_decision_2025-10-30T18:42:00Z",
                    "anomaly_id": "pulse_anomaly_pa_2025-10-30T16:12:34Z_0021",
                    "decision": "APPROVE",
                    "action": "ROUTE_TO_CANARY",
                    "intent": "Mitigate coverage_thin on cohort:mlb_2025_road_favorites",
                    "note": "Divert 20% to template_09 for coverage soak test",
                    "decided_by": "ops@loom",
                    "decided_at": "2025-10-30T18:42:00Z",
                    "links": {"anomaly_receipt": "pulse_anomaly_pa_2025-10-30T16:12:34Z_0021"},
                }
            ]
        }
    )

    receipt_type: str = "OperationsDecision"
    anomaly_id: str = Field(description="Stable Pulse anomaly identifier this decision addresses.")
    decision: Literal["APPROVE", "REFUSE"] = Field(
        description="Outcome of the triage decision for this anomaly."
    )
    action: str = Field(description="Action applied as part of the decision.")
    intent: str = Field(description="Operational intent or narrative for the action.")
    note: Optional[str] = Field(default=None, description="Optional free-form note or runbook reference.")
    decided_by: str = Field(description="Operator or agent who recorded the decision.")
    decided_at: datetime = Field(description="Timestamp when the decision was finalised.")
    links: Dict[str, Any] = Field(
        default_factory=dict,
        description="Optional mapping of related receipt identifiers or resources.",
    )
    context: Dict[str, Any] = Field(
        default_factory=dict,
        description="Supplemental context for downstream automation.",
    )

    @classmethod
    def validate(cls, data: Mapping[str, Any]) -> "OperationsDecisionReceipt":
        if isinstance(data, cls):
            return data
        return cls.model_validate(data)


class PolicyDecisionReceipt(GovernanceReceipt):
    """Receipt emitted when an authorization policy decision is evaluated."""

    receipt_type: str = "PolicyDecisionReceipt"
    policy_id: str
    policy_hash: Optional[str] = None
    bundle_sha256: Optional[str] = None
    bundle_version: Optional[str] = None
    action: str
    decision: Literal["allow", "deny"]
    actor: str
    input_hash: str
    context: Dict[str, Any] = Field(default_factory=dict)
    reason: Optional[str] = None
    decision_id: str


class AuthorizationRefusalReceipt(GovernanceReceipt):
    """Receipt emitted when a policy decision denies a requested action."""

    receipt_type: str = "AuthorizationRefusalReceipt"
    policy_id: str
    policy_hash: Optional[str] = None
    bundle_sha256: Optional[str] = None
    bundle_version: Optional[str] = None
    action: str
    actor: str
    reason: str
    input_hash: str
    required_claims: List[str] = Field(default_factory=list)
    decision_id: str


class ReviewActionReceipt(GovernanceReceipt):
    """Receipt emitted when a reviewer takes an action on a case."""

    receipt_type: str = "ReviewActionReceipt"
    target_receipt_id: str
    actor: str
    action: Literal["reviewed", "escalated", "comment"]
    comment: Optional[str] = None
    tags: List[str] = Field(default_factory=list)
    idempotency_key: Optional[str] = Field(default=None, description="Client-provided idempotency key")


class AccessAuditReceipt(GovernanceReceipt):
    """Receipt emitted when a sensitive read or access request is performed."""

    receipt_type: str = "AccessAuditReceipt"
    actor_id: str = Field(description="Identifier of the authenticated actor performing the access")
    role: str = Field(description="Role assigned to the actor at the time of access")
    scope: str = Field(description="Scope that authorised the access")
    resource: str = Field(description="Resource path or identifier accessed")
    action: str = Field(description="Logical action performed (e.g., HTTP verb)")
    ip: Optional[str] = Field(default=None, description="Source IP address if available")
    ok: bool = Field(default=True, description="Whether the access completed successfully")


class NonIntermixAuditReceipt(GovernanceReceipt):
    """Audit receipt capturing namespace isolation checks for shared memory shards."""

    receipt_type: str = "NonIntermixAuditReceipt"
    agent_namespace: str = Field(description="Agent namespace evaluated for non-intermix invariants")
    shard_root: str = Field(description="Absolute path to the namespace-specific memory shard root")
    shards: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="Per-shard stats including relative path and record counts",
    )
    sample_record_ids: List[str] = Field(
        default_factory=list,
        description="Sample of record identifiers observed during the audit (deduplicated)",
    )
    violations: List[str] = Field(
        default_factory=list,
        description="Namespace isolation violations detected during the audit",
    )
    passed: bool = Field(default=True, description="True when no violations were detected")


class DomainMigrationReceipt(GovernanceReceipt):
    """Audit record describing receipt class migrations between modules."""

    receipt_type: str = "DomainMigrationReceipt"
    migrated_classes: List[str] = Field(default_factory=list, description="Fully qualified class names moved")
    deprecated_classes: List[str] = Field(default_factory=list, description="Classes marked deprecated during move")
    source_module: str = Field(description="Original module containing the classes")
    destination_module: str = Field(description="Target module receiving the classes")
    file_paths: List[str] = Field(default_factory=list, description="Paths touched while migrating classes")
    metadata: Dict[str, str] = Field(default_factory=dict, description="Optional auxiliary metadata for audits")


class AttestationAnomaly(BaseModel):
    """Single anomaly detected during attestation sampling."""

    model_config = ConfigDictLike(frozen=True)

    receipt_id: str = Field(description="ID of the receipt with anomaly")
    issue: str = Field(description="Type of issue detected (e.g., missing_signature, mock_in_prod)")
    severity: str = Field(default="warning", description="Severity: info, warning, error")
    details: Dict[str, Any] = Field(default_factory=dict, description="Additional context about the anomaly")


class AttestationReceipt(GovernanceReceipt):
    """
    Receipt emitted by background attestor after sampling and verifying receipts.
    
    The attestor periodically samples receipts from the vault, runs trust
    verification via compute_trust_profile(), and emits this receipt with
    pass/fail counts and any anomalies detected.
    
    Use cases:
    - Continuous trust monitoring (immune system for the receipt spine)
    - Detect unsigned receipts, mock proofs in prod, TSA/Rekor issues
    - Audit trail for system health over time
    """

    receipt_type: str = "AttestationReceipt"
    
    # Sampling info
    sampled_count: int = Field(description="Number of receipts sampled")
    strata_sampled: List[str] = Field(default_factory=list, description="Vault strata sampled (e.g., warm, cold)")
    sample_strategy: str = Field(default="random", description="Sampling strategy used")
    sample_composition: Dict[str, int] = Field(
        default_factory=dict,
        description="Counts of sampled receipts by bucket (fresh/legacy/test)",
    )
    skip_reasons: Dict[str, int] = Field(
        default_factory=dict,
        description="Counts of skipped receipts by reason (legacy/test/error)",
    )
    
    # Results
    passed_count: int = Field(default=0, description="Receipts passing trust verification")
    failed_count: int = Field(default=0, description="Receipts failing trust verification")
    skipped_count: int = Field(default=0, description="Legacy/test receipts skipped (not evaluated)")
    court_grade_count: int = Field(default=0, description="Receipts achieving court-grade trust")
    mock_count: int = Field(default=0, description="Receipts with mock proofs detected")
    
    # Anomalies
    anomalies: List[AttestationAnomaly] = Field(
        default_factory=list,
        description="List of anomalies detected during attestation"
    )
    
    # Performance
    run_duration_ms: Optional[float] = Field(default=None, description="Duration of attestation run in milliseconds")
    
    # Status
    status: str = Field(default="ok", description="Overall status: ok, warning, error")
    error_message: Optional[str] = Field(default=None, description="Error message if status is error")


class GhostReceipt(GovernanceReceipt):
    """
    Counterfactual/ghost receipt representing a decision path not taken.
    
    Ghost receipts document what-if scenarios, alternative branches, and
    counterfactual reasoning. They are crucial for:
    
    - Audit: "What would have happened if we had done X?"
    - Learning: Training on alternative outcomes
    - Replay: Reconstructing decision trees from historical state
    - Compliance: Documenting that alternatives were considered
    
    CRITICAL: Ghost receipts must NEVER be fossilized in the vault.
    They represent hypothetical paths, not executed decisions.
    
    The `is_counterfactual` flag is always True for this receipt type.
    The `realized` flag indicates whether this ghost path was later
    actually taken (e.g., after a rollback or policy change).
    """

    receipt_type: str = "GhostReceipt"
    
    # Core counterfactual identity
    is_counterfactual: Literal[True] = Field(
        default=True,
        description="Always True for ghost receipts. Marks this as a hypothetical."
    )
    realized: bool = Field(
        default=False,
        description="Set to True if this counterfactual path was later actually executed."
    )
    
    # Source context
    source_receipt_id: str = Field(
        description="Receipt ID of the actual decision this is a counterfactual for."
    )
    branch_point: str = Field(
        description="Description of where the decision tree branched (e.g., 'gate:dignity_floor')."
    )
    
    # Alternative scenario
    alternative_decision: str = Field(
        description="The decision that would have been made in this counterfactual."
    )
    alternative_action: Optional[str] = Field(
        default=None,
        description="The action that would have been taken."
    )
    alternative_outcome: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Projected outcome if this path had been taken."
    )
    
    # Reasoning
    counterfactual_reason: str = Field(
        description="Why this alternative was considered (e.g., 'explore policy sensitivity')."
    )
    simulation_method: Optional[str] = Field(
        default=None,
        description="Method used to simulate the alternative (e.g., 'model_rollback', 'monte_carlo')."
    )
    
    # Impact projection
    projected_metrics: Dict[str, float] = Field(
        default_factory=dict,
        description="Projected metric values if this path had been taken."
    )
    delta_from_actual: Dict[str, float] = Field(
        default_factory=dict,
        description="Difference from actual outcome metrics (requires realized outcome)."
    )
    
    # Replay support
    policy_pin: Optional[Dict[str, str]] = Field(
        default=None,
        description="Policy version pin for deterministic replay of this counterfactual."
    )
    model_hash: Optional[str] = Field(
        default=None,
        description="Model hash used in the counterfactual simulation."
    )
    seed: Optional[int] = Field(
        default=None,
        description="Random seed for reproducible counterfactual simulation."
    )

    @model_validator(mode="after")
    def _enforce_ghost_invariants(self) -> "GhostReceipt":
        """Ensure ghost receipts are properly marked and cannot be accidentally fossilized."""
        # is_counterfactual must always be True
        if not self.is_counterfactual:
            object.__setattr__(self, "is_counterfactual", True)
        
        # Set a marker in context to prevent fossilization
        ctx = dict(self.context) if self.context else {}
        ctx["_no_fossilize"] = True
        ctx["_ghost_branch"] = self.branch_point
        object.__setattr__(self, "context", ctx)
        
        return self


# ============================================================================
# GenesisReceipt - Constitutional Physics ΔC Evaluation
# ============================================================================


# ============================================================================
# SuggestionReceipt - Constitutional Suggestion Evaluation
# ============================================================================


class SuggestionVerdict(str, Enum):
    """Verdict from governance evaluation."""
    ALLOW = "allow"
    WARN = "warn"
    BLOCK = "block"


class CoherenceSignals(BaseModel):
    """Coherence signals captured at decision time."""

    model_config = ConfigDictLike(frozen=True)

    coherence_score: float = Field(default=0.5, ge=0.0, le=1.0, description="Overall coherence score")
    alignment: float = Field(default=0.5, ge=0.0, le=1.0, description="Alignment with constitutional principles")
    calibration: float = Field(default=0.5, ge=0.0, le=1.0, description="Calibration confidence")
    confidence: float = Field(default=0.5, ge=0.0, le=1.0, description="Decision confidence")
    dignity: float = Field(default=0.8, ge=0.0, le=1.0, description="Dignity preservation score")


class ForecastMetadata(BaseModel):
    """Optional forecast attached to a governance receipt for calibration scoring."""

    model_config = ConfigDictLike(frozen=True)

    forecaster_id: Optional[str] = Field(
        default=None,
        description="Identifier of the forecaster (seat/agent/organelle) emitting this forecast.",
    )
    target_type: Optional[str] = Field(
        default=None,
        description="Receipt type that will serve as the outcome target for scoring (e.g., OutcomeReceipt).",
    )
    horizon_s: Optional[int] = Field(
        default=None,
        ge=0,
        description="Time horizon in seconds after which the forecast should be evaluated if no outcome has arrived.",
    )
    p_success: Optional[float] = Field(
        default=None,
        ge=0.0,
        le=1.0,
        description="Probability assigned by the forecaster that the intended outcome will succeed.",
    )


class SuggestionReceipt(GovernanceReceipt):
    """
    Receipt for constitutional suggestion evaluation.

    Emitted when a suggestion is evaluated through the governance pipeline:
    - WitnessStore case history lookup (episodic context)
    - Governor quality gates evaluation
    - Guardian constitutional vetting
    - Optional Council deliberation

    Captures full audit trail for constitutional self-observation.

    Usage:
        - Emitted by /api/suggestions endpoint
        - Contains governance verdicts, metrics, and coherence signals
        - Links to case history via witness_ids for episodic memory

    Semantics (SEMANTICS_RUNBOOK.md):
        - Structure: receipt (signed, replayable governance record)
        - Function: audit_log (suggestion evaluation trace)
        - Meaning: "Governance evaluated this suggestion with these results"
        - Dimensions:
          - Time: valid_time = evaluation moment
          - Scope: tenant-scoped (per-request)
          - Authority: Governor + Guardian + Council
          - Risk: varies by verdict (allow=low, warn=medium, block=high)
    """

    receipt_type: str = "SuggestionReceipt"

    # ─────────────────────────────────────────────────────────────────────────
    # Suggestion Content
    # ─────────────────────────────────────────────────────────────────────────
    suggestion_text: str = Field(
        min_length=1,
        max_length=10000,
        description="The suggestion text being evaluated",
    )
    case_id: Optional[str] = Field(
        default=None,
        description="Case ID for episodic context (links to WitnessStore history)",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Governance Verdicts
    # ─────────────────────────────────────────────────────────────────────────
    governor_verdict: SuggestionVerdict = Field(
        description="Final verdict from governance pipeline: allow|warn|block",
    )
    governor_reasons: List[str] = Field(
        default_factory=list,
        description="Reasons from Governor quality gates",
    )
    guardian_reasons: List[str] = Field(
        default_factory=list,
        description="Reasons from Guardian constitutional vetting",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Metrics & Coherence
    # ─────────────────────────────────────────────────────────────────────────
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Quality metrics: p_data, p_prior, ece_worst, coverage, dignity, etc.",
    )
    coherence: Optional[CoherenceSignals] = Field(
        default=None,
        description="Coherence signals at decision time",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Constitutional Metadata (Episodic Memory)
    # ─────────────────────────────────────────────────────────────────────────
    witness_ids: List[str] = Field(
        default_factory=list,
        description="Witness IDs from case history (episodic context)",
    )
    case_history_count: int = Field(
        default=0,
        ge=0,
        description="Number of prior receipts in case history",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Council Deliberation
    # ─────────────────────────────────────────────────────────────────────────
    council_used: bool = Field(
        default=False,
        description="Whether full council deliberation was used",
    )
    council_unanimous: Optional[bool] = Field(
        default=None,
        description="Council unanimity (if council was used)",
    )
    forecast: Optional[ForecastMetadata] = Field(
        default=None,
        description="Optional forecast for calibration (e.g., probability the decision holds without dispute).",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Tenant Context
    # ─────────────────────────────────────────────────────────────────────────
    tenant_id: str = Field(
        default="default",
        description="Tenant identifier for multi-tenant isolation",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Semantics Block
    # ─────────────────────────────────────────────────────────────────────────
    semantics: Dict[str, Any] = Field(
        default_factory=lambda: {
            "structural_type": "receipt",
            "functional_role": "audit_log",
            "meaning_tags": ["suggestion", "governance", "constitutional", "evaluation"],
            "scope": "tenant",
            "authority": "governor_guardian_council",
            "risk_level": "varies",
        },
        description="Semantic metadata: structure/function/meaning per SEMANTICS_RUNBOOK.md",
    )

    @model_validator(mode="after")
    def _update_risk_level(self) -> "SuggestionReceipt":
        """Update risk level in semantics based on verdict."""
        risk_map = {
            SuggestionVerdict.ALLOW: "low",
            SuggestionVerdict.WARN: "medium",
            SuggestionVerdict.BLOCK: "high",
        }
        semantics = dict(self.semantics)
        semantics["risk_level"] = risk_map.get(self.governor_verdict, "medium")
        object.__setattr__(self, "semantics", semantics)
        return self


class GovernorDecisionReceipt(GovernanceReceipt):
    """
    Receipt for Governor quality gate evaluation.

    Minted when the Governor evaluates a prediction through quality gates:
    - Calibration (worst-slice ECE)
    - EVHD monotonicity
    - Coherence triangulation
    - Dignity constraints

    This receipt creates edges in the mycelium for lineage tracking.

    Usage:
        - Emitted by governor_service.py on /decide endpoint
        - Links to parent receipts via parent_ids
        - Downstream Guardian/Genesis receipts can link back to this

    Semantics (SEMANTICS_RUNBOOK.md):
        - Structure: receipt (signed, replayable governance record)
        - Function: audit_log (quality gate evaluation trace)
        - Meaning: "Governor evaluated prediction quality with these results"
        - Dimensions:
          - Time: valid_time = evaluation moment
          - Scope: tenant-scoped (per-request)
          - Authority: Governor quality gate system
          - Risk: varies by decision (ISSUE=low, REFUSE=high)
    """

    receipt_type: str = "GovernorDecisionReceipt"

    # ─────────────────────────────────────────────────────────────────────────
    # Core Decision
    # ─────────────────────────────────────────────────────────────────────────
    decision: str = Field(
        description="Gate verdict: ISSUE or REFUSE",
    )
    reasons: List[str] = Field(
        default_factory=list,
        description="Reasons for REFUSE (empty for ISSUE)",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Quality Metrics
    # ─────────────────────────────────────────────────────────────────────────
    metrics: Dict[str, Any] = Field(
        default_factory=dict,
        description="Gate metrics: pstar, sigma, ece_worst, evhd_rho, coherence_mean_score, etc.",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Context
    # ─────────────────────────────────────────────────────────────────────────
    case_id: Optional[str] = Field(
        default=None,
        description="Case/PA ID for episodic memory tracking",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Recourse
    # ─────────────────────────────────────────────────────────────────────────
    recourse_paths: Optional[Dict[str, str]] = Field(
        default=None,
        description="Recourse guidance if dignity constraints fail",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Semantics Block
    # ─────────────────────────────────────────────────────────────────────────
    semantics: Dict[str, Any] = Field(
        default_factory=lambda: {
            "structural_type": "receipt",
            "functional_role": "audit_log",
            "meaning_tags": ["governor", "quality_gate", "calibration", "coherence"],
            "scope": "tenant",
            "authority": "governor_quality_gate",
            "risk_level": "low",  # Adjusted based on decision
        },
        description="Semantic metadata: structure/function/meaning per SEMANTICS_RUNBOOK.md",
    )


class GenesisStatus(str, Enum):
    """Status from Genesis ΔC computation."""
    OK = "ok"
    DEGRADED = "degraded"
    VALIDATOR_UNTRUSTED = "validator_untrusted"
    DIGNITY_FLOOR = "dignity_floor"
    COHERENCE_FLOOR = "coherence_floor"
    PARADOX_VIOLATION = "paradox_violation"
    HYSTERESIS_PENDING = "hysteresis_pending"


class GenesisReceipt(GovernanceReceipt):
    """
    Receipt for Genesis constitutional physics (ΔC) evaluation.

    Minted when Guardian evaluates a suggestion with Genesis backend enabled.
    Records the coherence delta computation and constitutional constraint checks.

    Constitutional spine: Implements Equation 0 observability -
    ΔC = f(Λ_D, Ψ_para, Π_mem, Ξ_emer) subject to Ω_coll

    Usage:
        - Emitted by guardian_service.py when GENESIS_BACKEND != disabled
        - Contains full component breakdown for interpretability
        - Links to parent Guardian decision receipt

    Semantics (SEMANTICS_RUNBOOK.md):
        - Structure: receipt (signed, replayable governance record)
        - Function: audit_log (constitutional physics evaluation trace)
        - Meaning: "Genesis evaluated coherence delta for this decision"
        - Dimensions:
          - Time: valid_time = evaluation moment
          - Scope: tenant-scoped (per-request)
          - Authority: Genesis backend (constitutional physics layer)
          - Risk: low (observability only) in shadow mode, high in enforce mode
    """

    receipt_type: str = "GenesisReceipt"

    # ─────────────────────────────────────────────────────────────────────────
    # Core Result
    # ─────────────────────────────────────────────────────────────────────────
    delta_c: Optional[float] = Field(
        default=None,
        ge=-1.0,
        le=1.0,
        description="Computed coherence delta [-1, 1], None if validator untrusted",
    )
    status: GenesisStatus = Field(
        description="Result status: ok|degraded|validator_untrusted|dignity_floor|coherence_floor|paradox_violation|hysteresis_pending",
    )
    should_allow: bool = Field(
        description="Genesis verdict: True if action should be allowed",
    )
    should_allow_strict: Optional[bool] = Field(
        default=None,
        description="Conservative verdict (degraded advisory-only, ignores hysteresis overrides)",
    )
    is_advisory: bool = Field(
        default=False,
        description="True if result is advisory only (shadow mode or degraded validator)",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Input State
    # ─────────────────────────────────────────────────────────────────────────
    lambda_d: float = Field(
        ge=0.0,
        le=1.0,
        description="Dignity floor Λ_D input [0, 1]",
    )
    psi: float = Field(
        ge=0.0,
        le=1.0,
        description="Paradox intensity Ψ input [0, 1]",
    )
    pi: float = Field(
        ge=0.0,
        le=1.0,
        description="Memory pressure Π input [0, 1]",
    )
    xi: float = Field(
        ge=0.0,
        le=1.0,
        description="Emergence rate Ξ input [0, 1]",
    )
    omega: float = Field(
        ge=0.0,
        le=1.0,
        description="Collective coherence Ω input [0, 1]",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Component Breakdown
    # ─────────────────────────────────────────────────────────────────────────
    components: Dict[str, Any] = Field(
        default_factory=dict,
        description="Breakdown of contributions/penalties; may include hysteresis counters/pending flags",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Violations
    # ─────────────────────────────────────────────────────────────────────────
    violations: List[str] = Field(
        default_factory=list,
        description="Constitutional violations detected during evaluation",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Hysteresis Observability
    # ─────────────────────────────────────────────────────────────────────────
    hysteresis_pending: Optional[bool] = Field(
        default=None,
        description="True if enforcement is delayed by hysteresis (pending window)",
    )
    hysteresis_base_status: Optional[str] = Field(
        default=None,
        description="Underlying constitutional status before hysteresis override",
    )
    hysteresis_consecutive_bad: Optional[int] = Field(
        default=None,
        description="Consecutive bad cycles observed for hysteresis gating",
    )
    hysteresis_required: Optional[int] = Field(
        default=None,
        description="Hysteresis threshold (cycles required before enforcement)",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Backend Mode
    # ─────────────────────────────────────────────────────────────────────────
    backend_mode: str = Field(
        default="disabled",
        description="Genesis backend mode: disabled|shadow|enforce",
    )
    validator_health: str = Field(
        default="unknown",
        description="Validator trust level: healthy|degraded|unknown",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Signal Provenance (Phase 1)
    # ─────────────────────────────────────────────────────────────────────────
    signal_provenance: Dict[str, str] = Field(
        default_factory=dict,
        description="Source of each signal: genesis_state|dignity_vector|omega_c|buffer_tempo|coherence_check|memory|allow_rate|default",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Linkage
    # ─────────────────────────────────────────────────────────────────────────
    guardian_decision_id: Optional[str] = Field(
        default=None,
        description="Parent Guardian decision receipt ID",
    )
    case_id: Optional[str] = Field(
        default=None,
        description="Case/PA ID for episodic memory tracking",
    )

    # ─────────────────────────────────────────────────────────────────────────
    # Semantics Block
    # ─────────────────────────────────────────────────────────────────────────
    semantics: Dict[str, Any] = Field(
        default_factory=lambda: {
            "structural_type": "receipt",
            "functional_role": "audit_log",
            "meaning_tags": ["genesis", "delta_c", "constitutional_physics", "coherence"],
            "scope": "tenant",
            "authority": "genesis_backend",
            "risk_level": "low",  # Overridden to "high" when enforce mode
        },
        description="Semantic metadata: structure/function/meaning per SEMANTICS_RUNBOOK.md",
    )


class BackoffAdjustmentReceipt(GovernanceReceipt):
    """Receipt emitted when Recovery learner adjusts task retry backoff policy."""

    receipt_type: str = "BackoffAdjustmentReceipt"
    organ: str = Field(default="recovery")

    task_type: str = Field(description="Task type whose backoff is being adjusted.")
    error_classification: str = Field(
        description="Error type that triggered adjustment (TRANSIENT, RESOURCE_EXHAUSTED, PERMANENT, UNKNOWN)."
    )
    old_backoff_ms: float = Field(
        description="Previous backoff delay in milliseconds."
    )
    new_backoff_ms: float = Field(
        description="New backoff delay in milliseconds."
    )
    adjustment_reason: str = Field(
        description="Why the backoff changed (e.g., 'failure_pattern_detected', 'resource_exhaustion_trend')."
    )
    confidence: float = Field(
        ge=0.0,
        le=1.0,
        description="Recovery learner confidence in this adjustment.",
    )
    supporting_observations: int = Field(
        description="Number of failure observations supporting this change."
    )
    is_monotonic_increase: bool = Field(
        description="True if backoff only increased (always safe). False if relaxed (requires approval)."
    )


class PolicyVotingRecord(GovernanceReceipt):
    """Record of a vote cast for a proposed policy change via the voting gate."""

    receipt_type: str = "PolicyVotingRecord"
    organ: str = Field(default="governance")

    proposal_id: str = Field(description="Identifier of the policy proposal being voted on.")
    voter_name: str = Field(description="Name of the module/component casting the vote.")
    vote_value: Literal["APPROVE", "REJECT", "ABSTAIN"] = Field(
        description="Vote cast: APPROVE, REJECT, or ABSTAIN."
    )
    supporting_evidence: Dict[str, Any] = Field(
        default_factory=dict,
        description="Evidence/metrics justifying this vote.",
    )
    confidence: float = Field(
        ge=0.0,
        le=1.0,
        description="Voter confidence in this vote (0.0-1.0).",
    )
    timestamp_cast: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="Timestamp when vote was cast.",
    )


class PolicyRollbackToken(GovernanceReceipt):
    """Token enabling rapid rollback of a policy change if it causes problems."""

    receipt_type: str = "PolicyRollbackToken"
    organ: str = Field(default="governance")

    applied_policy_update_id: str = Field(
        description="Receipt ID of the PolicyUpdateReceipt that was applied."
    )
    rule_id: str = Field(description="Policy rule that was changed.")
    previous_value: Any = Field(description="Previous value (rollback target).")
    current_value: Any = Field(description="Current value after policy update.")
    rollback_deadline: datetime = Field(
        description="Latest timestamp until which rollback is available."
    )
    rollback_reason_template: str = Field(
        default="Policy rollback initiated automatically",
        description="Template message for rollback justification.",
    )
    requires_approval: bool = Field(
        default=False,
        description="If True, rollback requires human/Guardian approval.",
    )
    emergency_contact: Optional[str] = Field(
        default=None,
        description="Contact identifier if emergency rollback needed.",
    )


__all__ = [
    "GovernanceReceipt",
    "DomainMigrationReceipt",
    "SuggestionVerdict",
    "CoherenceSignals",
    "SuggestionReceipt",
    "GovernorDecisionReceipt",
    "GenesisStatus",
    "GenesisReceipt",
    "PolicyRecommendation",
    "PolicyImpactProjection",
    "PolicyPreflight",
    "PolicyFreezeProposal",
    "PolicyUnfreezeProposal",
    "FreezeDecisionReceipt",
    "ConstitutionalSnapshot",
    "DriftType",
    "DriftAlert",
    "GateTuningReceipt",
    "OverrideDisposition",
    "OverrideQualityVerdict",
    "GuardianApprovalStatus",
    "MultiSigApprovalState",
    "OverrideRiskLevel",
    "GuardianSignature",
    "OverrideQualityReceipt",
    "OverrideReceipt",
    "VetoQueueReceipt",
    "DissentReceipt",
    "VetoDecisionReceipt",
    "MultiSigGuardianApproval",
    "PolicyChangeReceipt",
    "GatebookChangeReceipt",
    "OptimizationGuardReceipt",
    "EvidenceQuorumReceipt",
    "ComplianceAssertionReceipt",
    "QuorumSatisfiedReceipt",
    "OperationsDecisionReceipt",
    "PolicyDecisionReceipt",
    "AuthorizationRefusalReceipt",
    "ReviewActionReceipt",
    "AccessAuditReceipt",
    "NonIntermixAuditReceipt",
    "AttestationAnomaly",
    "AttestationReceipt",
    "GhostReceipt",
    "TimestampPolicyReceipt",
    "AnchoringBatchWindow",
    "AnchoringBatchCounts",
    "AnchoringBatchReceipt",
    "BackoffAdjustmentReceipt",
    "PolicyVotingRecord",
    "PolicyRollbackToken",
]
