//! Response DTOs for query results and API operations. use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use super::enums::{LifecycleDto, ObjectValueDto, SignatureDto, SourceClassDto}; // ============================================================================ // Source Status Warning (P3.2 Cascade Flagging) // ============================================================================ /// Warning attached to assertions citing non-Active sources. /// /// Enables "show with warning" behavior per P3.2 requirements. /// When a source is deprecated or quarantined, assertions citing it /// receive this warning instead of being silently filtered. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct SourceWarningDto { /// Warning type: "deprecated" or "quarantined" pub warning_type: String, /// Human-readable explanation pub message: String, /// Source label from registry (if available) #[serde(skip_serializing_if = "Option::is_none")] pub source_label: Option, /// When the source status was last updated (Unix timestamp) pub status_updated_at: u64, } // ============================================================================ // Response DTOs // ============================================================================ /// Response containing a single assertion. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct AssertionResponse { /// Content-addressed hash of this assertion (hex-encoded) pub hash: String, /// The subject entity pub subject: String, /// The predicate/relation pub predicate: String, /// The object value pub object: ObjectValueDto, /// Hash of parent assertion (hex-encoded, optional) #[serde(skip_serializing_if = "Option::is_none")] pub parent_hash: Option, /// Hash of source evidence (hex-encoded) pub source_hash: String, /// Source authority tier pub source_class: SourceClassDto, /// Perceptual hash for visual anchoring (hex-encoded, optional) #[serde(skip_serializing_if = "Option::is_none")] pub visual_hash: Option, /// Epoch ID (hex-encoded, optional) #[serde(skip_serializing_if = "Option::is_none")] pub epoch: Option, /// Lifecycle stage pub lifecycle: LifecycleDto, /// Agent signatures pub signatures: Vec, /// Confidence score (0.0 to 1.0) pub confidence: f32, /// Creation timestamp (Unix epoch) pub timestamp: u64, /// Semantic embedding vector (optional) #[serde(skip_serializing_if = "Option::is_none")] pub vector: Option>, /// Structured source metadata as a JSON string (optional). /// Domain-specific schema (journal, DOI, sample_size, etc.). #[serde(skip_serializing_if = "Option::is_none")] pub source_metadata: Option, /// Free-text narrative explaining methodology, limitations, bias, and caveats. #[serde(skip_serializing_if = "Option::is_none")] pub narrative: Option, /// Warning if this assertion cites a quarantined or deprecated source. /// /// Present when the assertion's source has a non-Active status in the /// Source Registry. Enables "show with warning" behavior for enterprise /// compliance workflows. #[serde(skip_serializing_if = "Option::is_none")] pub source_warning: Option, } /// Response from a query operation. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct QueryResponse { /// Matching assertions pub assertions: Vec, /// Total number of results returned pub total_count: usize, /// Whether there are more results beyond the limit pub has_more: bool, /// Degree of disagreement among candidates (0.0 = unanimous, 1.0 = max conflict). /// See `stemedb_lens::compute_conflict_score()` for the canonical algorithm. /// Only present when a lens is applied. #[serde(skip_serializing_if = "Option::is_none")] pub conflict_score: Option, /// Confidence in the resolution (0.0 to 1.0). /// Only present when a lens is applied. #[serde(skip_serializing_if = "Option::is_none")] pub resolution_confidence: Option, /// Changelog entries for MV changes since the `since` parameter. /// Only present when `since` parameter was provided. #[serde(skip_serializing_if = "Option::is_none")] pub changes_since: Option>, } /// A changelog entry from a "since" query. /// /// Represents a single change to a materialized view's winner. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ChangeEntryDto { /// Unix timestamp when the change occurred. pub timestamp: u64, /// Hash of the previous winner (hex-encoded, None if first MV). #[serde(skip_serializing_if = "Option::is_none")] pub previous_winner_hash: Option, /// Hash of the new winner (hex-encoded). pub new_winner_hash: String, /// Subject of the changed pair. pub subject: String, /// Predicate of the changed pair. pub predicate: String, /// Which lens was used for resolution. pub lens_name: String, } /// Error response. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ErrorResponse { /// Human-readable error message pub error: String, /// Machine-readable error code pub code: String, } /// Health check response. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct HealthResponse { /// Service status (e.g., "healthy") pub status: String, /// API version pub version: String, /// Total number of assertions in the database pub assertions_count: u64, } /// Response from retrieving a source document. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ProvenanceResponse { /// BLAKE3 hash of the content (hex-encoded). pub hash: String, /// The source document content (base64-encoded). pub content: String, /// MIME type of the content. pub content_type: String, /// Size of the content in bytes. pub size: usize, } /// Per-tier resolution result from LayeredConsensus lens. /// /// Represents the consensus within a single source class tier. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct TierResolutionDto { /// The tier number (0-5). Lower = higher authority. pub tier: u8, /// The source class for this tier. pub source_class: SourceClassDto, /// The winning assertion from within-tier consensus, if any candidates. #[serde(skip_serializing_if = "Option::is_none")] pub winner: Option, /// Number of candidates in this tier. pub candidates_count: usize, /// Within-tier conflict score (0.0 = unanimous, 1.0 = max conflict). #[schema(minimum = 0.0, maximum = 1.0)] pub conflict_score: f32, /// Within-tier resolution confidence (0.0 to 1.0). #[schema(minimum = 0.0, maximum = 1.0)] pub resolution_confidence: f32, } /// Response from the admin rebuild-indexes endpoint. /// /// Reports how many assertion indexes were rebuilt, how many were /// skipped (e.g., deserialization failures), and how long the /// operation took. #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct RebuildIndexesResponse { /// Number of assertions whose indexes were rebuilt. pub rebuilt_count: u64, /// Number of keys that were skipped (deserialization failures). pub skipped_count: u64, /// Wall-clock time for the operation in milliseconds. pub elapsed_ms: u64, /// Human-readable status message. pub status: String, /// First error encountered (for diagnostics). Absent when all succeed. #[serde(skip_serializing_if = "Option::is_none")] pub first_error: Option, } /// Response from a LayeredConsensus query. /// /// Provides per-tier resolution results plus an overall winner. /// Use this to see "What does Tier 0 say? What does Tier 5 say?" #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct LayeredQueryResponse { /// The subject that was queried. pub subject: String, /// The predicate that was queried. pub predicate: String, /// Per-tier consensus results, ordered by tier (0 = highest authority first). /// Only tiers with at least one candidate are included. pub tiers: Vec, /// Overall winner: winner from the highest-authority tier that has candidates. #[serde(skip_serializing_if = "Option::is_none")] pub overall_winner: Option, /// Cross-tier disagreement score (0.0 = tiers agree, 1.0 = tiers disagree). #[schema(minimum = 0.0, maximum = 1.0)] pub overall_conflict_score: f32, /// Total candidates considered across all tiers. pub total_candidates: usize, /// Unix timestamp when this view was computed. pub computed_at: u64, /// Which lens was used (always "LayeredConsensus"). pub lens_name: String, }