cellstate_core/
enums.rs

1//! Enum types for CELLSTATE entities
2//!
3//! ## Database string convention (from_db_str / as_db_str)
4//!
5//! Reading from the database accepts both legacy (PascalCase or mixed-case) and
6//! new (snake_case) values for backward compatibility; **writing uses snake_case
7//! only** via `as_db_str()`. Do not remove or simplify `from_db_str()` normalization
8//! without a migration that updates existing enum columns to snake_case.
9
10use crate::DurationMs;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use std::str::FromStr;
14
15// ============================================================================
16// UNIFIED ENUM PARSE ERROR
17// ============================================================================
18
19/// Error returned when parsing a DB enum string fails.
20///
21/// Replaces the 17+ individual `*ParseError` structs that had identical
22/// `Display` and `Error` implementations. The `enum_name` field carries the
23/// name of the target enum so error messages remain specific.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct EnumParseError {
26    /// Name of the enum type that was being parsed (e.g. "AgentStatus").
27    pub enum_name: &'static str,
28    /// The raw input string that could not be parsed.
29    pub input: String,
30}
31
32impl fmt::Display for EnumParseError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "Unknown {} value: '{}'", self.enum_name, self.input)
35    }
36}
37
38impl std::error::Error for EnumParseError {}
39
40// ============================================================================
41// CORE ENUMS
42// ============================================================================
43
44/// Time-to-live and retention configuration for memory entries.
45/// Supports both time-based expiration and count-based limits.
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[serde(rename_all = "snake_case")]
48#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
49pub enum TTL {
50    /// Never expires
51    Persistent,
52    /// Expires when session ends
53    Session,
54    /// Expires when scope closes
55    Scope,
56    /// Expires after specified duration in milliseconds
57    Duration(DurationMs),
58    /// Ephemeral - expires when scope closes (alias for Scope)
59    Ephemeral,
60    /// Short-term retention (~1 hour)
61    ShortTerm,
62    /// Medium-term retention (~24 hours)
63    MediumTerm,
64    /// Long-term retention (~7 days)
65    LongTerm,
66    /// Permanent - never expires (alias for Persistent)
67    Permanent,
68    /// Keep at most N entries (count-based retention from pack config)
69    Max(usize),
70}
71
72/// Entity type discriminator for polymorphic references.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
76pub enum EntityType {
77    Trajectory,
78    Scope,
79    Artifact,
80    Note,
81    Turn,
82    Tenant,
83    AgentWorkingSet,
84    Link,
85    Belief,
86    Goal,
87    Plan,
88    Lock,
89    Message,
90    Agent,
91    Delegation,
92    Handoff,
93    Config,
94    Conflict,
95    Edge,
96    EvolutionSnapshot,
97    SummarizationPolicy,
98    SummarizationRequest,
99    ToolExecution,
100    /// Event (event DAG / storage context)
101    Event,
102}
103
104/// Memory category for hierarchical memory organization.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
106#[serde(rename_all = "snake_case")]
107#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
108pub enum MemoryCategory {
109    Ephemeral,
110    Working,
111    Episodic,
112    Semantic,
113    Procedural,
114    Meta,
115}
116
117/// Principal type for identity and ownership scoping.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
119#[serde(rename_all = "snake_case")]
120#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
121pub enum PrincipalType {
122    User,
123    Agent,
124    System,
125}
126
127impl PrincipalType {
128    /// Convert to database string representation.
129    pub fn as_db_str(&self) -> &'static str {
130        match self {
131            PrincipalType::User => "user",
132            PrincipalType::Agent => "agent",
133            PrincipalType::System => "system",
134        }
135    }
136}
137
138impl fmt::Display for PrincipalType {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        f.write_str(self.as_db_str())
141    }
142}
143
144/// Agent type discriminator for registration and message routing.
145/// Extensible via Custom for user-defined agent kinds.
146#[derive(Debug, Clone, PartialEq, Eq, Hash)]
147#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
148pub enum AgentType {
149    Tester,
150    Monitor,
151    Planner,
152    Executor,
153    Reviewer,
154    Custom(String),
155}
156
157impl AgentType {
158    pub fn as_str(&self) -> &str {
159        match self {
160            AgentType::Tester => "tester",
161            AgentType::Monitor => "monitor",
162            AgentType::Planner => "planner",
163            AgentType::Executor => "executor",
164            AgentType::Reviewer => "reviewer",
165            AgentType::Custom(s) => s.as_str(),
166        }
167    }
168}
169
170impl Serialize for AgentType {
171    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172    where
173        S: serde::Serializer,
174    {
175        serializer.serialize_str(self.as_str())
176    }
177}
178
179impl<'de> Deserialize<'de> for AgentType {
180    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181    where
182        D: serde::Deserializer<'de>,
183    {
184        let s = String::deserialize(deserializer)?;
185        Ok(match s.to_lowercase().as_str() {
186            "tester" => AgentType::Tester,
187            "monitor" => AgentType::Monitor,
188            "planner" => AgentType::Planner,
189            "executor" => AgentType::Executor,
190            "reviewer" => AgentType::Reviewer,
191            _ => AgentType::Custom(s),
192        })
193    }
194}
195
196impl fmt::Display for AgentType {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.write_str(self.as_str())
199    }
200}
201
202impl FromStr for AgentType {
203    type Err = std::convert::Infallible;
204
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        Ok(match s.to_lowercase().as_str() {
207            "tester" => AgentType::Tester,
208            "monitor" => AgentType::Monitor,
209            "planner" => AgentType::Planner,
210            "executor" => AgentType::Executor,
211            "reviewer" => AgentType::Reviewer,
212            _ => AgentType::Custom(s.to_string()),
213        })
214    }
215}
216
217impl From<&str> for AgentType {
218    fn from(s: &str) -> Self {
219        match AgentType::from_str(s) {
220            Ok(value) => value,
221            Err(never) => match never {},
222        }
223    }
224}
225
226/// Memory type for permission scoping (artifact, note, turn, working set).
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
228#[serde(rename_all = "snake_case")]
229#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
230pub enum MemoryType {
231    Artifact,
232    Note,
233    Turn,
234    WorkingSet,
235    /// Wildcard: all memory types (wire format "*" for backward compatibility).
236    #[serde(rename = "*")]
237    All,
238}
239
240impl MemoryType {
241    pub fn as_db_str(&self) -> &'static str {
242        match self {
243            MemoryType::Artifact => "artifact",
244            MemoryType::Note => "note",
245            MemoryType::Turn => "turn",
246            MemoryType::WorkingSet => "working_set",
247            MemoryType::All => "*",
248        }
249    }
250}
251
252/// Resource type for lock scoping.
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
254#[serde(rename_all = "snake_case")]
255#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
256pub enum ResourceType {
257    Trajectory,
258    Scope,
259    Artifact,
260    Note,
261    Agent,
262}
263
264impl ResourceType {
265    pub fn as_db_str(&self) -> &'static str {
266        match self {
267            ResourceType::Trajectory => "trajectory",
268            ResourceType::Scope => "scope",
269            ResourceType::Artifact => "artifact",
270            ResourceType::Note => "note",
271            ResourceType::Agent => "agent",
272        }
273    }
274}
275
276impl FromStr for ResourceType {
277    type Err = String;
278
279    fn from_str(s: &str) -> Result<Self, Self::Err> {
280        match normalize_token(s).as_str() {
281            "trajectory" => Ok(ResourceType::Trajectory),
282            "scope" => Ok(ResourceType::Scope),
283            "artifact" => Ok(ResourceType::Artifact),
284            "note" => Ok(ResourceType::Note),
285            "agent" => Ok(ResourceType::Agent),
286            _ => Err(format!("Invalid ResourceType: {}", s)),
287        }
288    }
289}
290
291/// Field types for schema definitions.
292/// Used by pack compiler and runtime validation.
293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
294#[serde(rename_all = "snake_case")]
295#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
296pub enum FieldType {
297    /// UUID identifier
298    Uuid,
299    /// Text/string
300    Text,
301    /// Integer
302    Int,
303    /// Floating point
304    Float,
305    /// Boolean
306    Bool,
307    /// Timestamp
308    Timestamp,
309    /// JSON blob
310    Json,
311    /// Vector embedding with optional dimension hint
312    Embedding { dimensions: Option<usize> },
313    /// Enumeration with named variants
314    Enum { variants: Vec<String> },
315    /// Array of another field type
316    Array(Box<FieldType>),
317}
318
319/// Status of a trajectory (task container).
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
321#[serde(rename_all = "snake_case")]
322#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
323pub enum TrajectoryStatus {
324    Active,
325    Completed,
326    Failed,
327    Suspended,
328}
329
330/// Outcome status for completed trajectories.
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
332#[serde(rename_all = "snake_case")]
333#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
334pub enum OutcomeStatus {
335    Success,
336    Partial,
337    Failure,
338}
339
340/// Status of an agent in the system.
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
342#[serde(rename_all = "snake_case")]
343#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
344pub enum AgentStatus {
345    /// Agent is registered but not actively working
346    #[default]
347    Idle,
348    /// Agent is actively processing a task
349    Active,
350    /// Agent is blocked waiting on something (lock, delegation, etc.)
351    Blocked,
352    /// Agent has failed and requires attention
353    Failed,
354    /// Agent has been unregistered
355    Offline,
356}
357
358impl AgentStatus {
359    /// Convert to database string representation (lowercase to match DB CHECK constraints).
360    pub fn as_db_str(&self) -> &'static str {
361        match self {
362            AgentStatus::Idle => "idle",
363            AgentStatus::Active => "active",
364            AgentStatus::Blocked => "blocked",
365            AgentStatus::Failed => "failed",
366            AgentStatus::Offline => "offline",
367        }
368    }
369
370    /// Parse from database string representation.
371    pub fn from_db_str(s: &str) -> Result<Self, EnumParseError> {
372        match s.to_lowercase().as_str() {
373            "idle" => Ok(AgentStatus::Idle),
374            "active" => Ok(AgentStatus::Active),
375            "blocked" => Ok(AgentStatus::Blocked),
376            "failed" => Ok(AgentStatus::Failed),
377            "offline" => Ok(AgentStatus::Offline),
378            _ => Err(EnumParseError {
379                enum_name: "AgentStatus",
380                input: s.to_string(),
381            }),
382        }
383    }
384
385    /// Check if the agent can accept new work.
386    pub fn can_accept_work(&self) -> bool {
387        matches!(self, AgentStatus::Idle)
388    }
389}
390
391impl fmt::Display for AgentStatus {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        write!(f, "{}", self.as_db_str())
394    }
395}
396
397impl FromStr for AgentStatus {
398    type Err = EnumParseError;
399
400    fn from_str(s: &str) -> Result<Self, Self::Err> {
401        Self::from_db_str(s)
402    }
403}
404
405/// Status of a summarization request.
406#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
407#[serde(rename_all = "snake_case")]
408#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
409pub enum SummarizationRequestStatus {
410    Pending,
411    InProgress,
412    Completed,
413    Failed,
414}
415
416impl SummarizationRequestStatus {
417    pub fn as_db_str(&self) -> &'static str {
418        match self {
419            SummarizationRequestStatus::Pending => "pending",
420            SummarizationRequestStatus::InProgress => "in_progress",
421            SummarizationRequestStatus::Completed => "completed",
422            SummarizationRequestStatus::Failed => "failed",
423        }
424    }
425
426    pub fn from_db_str(s: &str) -> Result<Self, EnumParseError> {
427        match s.to_lowercase().as_str() {
428            "pending" => Ok(SummarizationRequestStatus::Pending),
429            "inprogress" | "in_progress" | "in-progress" => {
430                Ok(SummarizationRequestStatus::InProgress)
431            }
432            "completed" | "complete" => Ok(SummarizationRequestStatus::Completed),
433            "failed" | "failure" => Ok(SummarizationRequestStatus::Failed),
434            _ => Err(EnumParseError {
435                enum_name: "SummarizationRequestStatus",
436                input: s.to_string(),
437            }),
438        }
439    }
440}
441
442impl fmt::Display for SummarizationRequestStatus {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        write!(f, "{}", self.as_db_str())
445    }
446}
447
448impl FromStr for SummarizationRequestStatus {
449    type Err = EnumParseError;
450
451    fn from_str(s: &str) -> Result<Self, Self::Err> {
452        Self::from_db_str(s)
453    }
454}
455
456/// Status of a tool execution.
457#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
458#[serde(rename_all = "snake_case")]
459#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
460pub enum ToolExecutionStatus {
461    Started,
462    Succeeded,
463    Failed,
464}
465
466impl ToolExecutionStatus {
467    pub fn as_db_str(&self) -> &'static str {
468        match self {
469            ToolExecutionStatus::Started => "started",
470            ToolExecutionStatus::Succeeded => "succeeded",
471            ToolExecutionStatus::Failed => "failed",
472        }
473    }
474
475    pub fn from_db_str(s: &str) -> Result<Self, EnumParseError> {
476        match s.to_lowercase().as_str() {
477            "started" | "start" => Ok(ToolExecutionStatus::Started),
478            "succeeded" | "success" => Ok(ToolExecutionStatus::Succeeded),
479            "failed" | "failure" => Ok(ToolExecutionStatus::Failed),
480            _ => Err(EnumParseError {
481                enum_name: "ToolExecutionStatus",
482                input: s.to_string(),
483            }),
484        }
485    }
486}
487
488impl fmt::Display for ToolExecutionStatus {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        write!(f, "{}", self.as_db_str())
491    }
492}
493
494impl FromStr for ToolExecutionStatus {
495    type Err = EnumParseError;
496
497    fn from_str(s: &str) -> Result<Self, Self::Err> {
498        Self::from_db_str(s)
499    }
500}
501
502/// Role of a turn in conversation.
503#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
504#[serde(rename_all = "snake_case")]
505#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
506pub enum TurnRole {
507    User,
508    Assistant,
509    System,
510    Tool,
511}
512
513/// Type of artifact produced during a trajectory.
514#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
515#[serde(rename_all = "snake_case")]
516#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
517pub enum ArtifactType {
518    ErrorLog,
519    CodePatch,
520    DesignDecision,
521    UserPreference,
522    Fact,
523    Constraint,
524    ToolResult,
525    IntermediateOutput,
526    Custom,
527    Code,
528    Document,
529    Data,
530    Model,
531    Config,
532    Log,
533    Summary,
534    Decision,
535    Plan,
536    // Multimodal artifact types
537    Audio,
538    Image,
539    Video,
540    Transcript,
541    Screenshot,
542}
543
544/// Method used to extract an artifact or evidence.
545#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
546#[serde(rename_all = "snake_case")]
547#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
548pub enum ExtractionMethod {
549    /// Explicitly provided by user
550    Explicit,
551    /// Inferred from context
552    Inferred,
553    /// User provided directly
554    UserProvided,
555    /// Extracted by LLM
556    LlmExtraction,
557    /// Extracted by tool/function
558    ToolExtraction,
559    /// From memory recall
560    MemoryRecall,
561    /// From external API
562    ExternalApi,
563    /// Unknown or unspecified
564    #[default]
565    Unknown,
566}
567
568/// Type of note (cross-trajectory knowledge).
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
570#[serde(rename_all = "snake_case")]
571#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
572pub enum NoteType {
573    Convention,
574    Strategy,
575    Gotcha,
576    Fact,
577    Preference,
578    Relationship,
579    Procedure,
580    Meta,
581    Insight,
582    Correction,
583    Summary,
584}
585
586// ============================================================================
587// BATTLE INTEL ENUMS
588// ============================================================================
589
590/// LLM provider type for configuration.
591#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
592#[serde(rename_all = "snake_case")]
593#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
594pub enum ProviderType {
595    #[serde(rename = "openai")]
596    OpenAI,
597    Anthropic,
598    OpenRouter,
599    Local,
600    Custom,
601}
602
603impl ProviderType {
604    /// Convert to database string representation.
605    pub fn as_db_str(&self) -> &'static str {
606        match self {
607            ProviderType::OpenAI => "openai",
608            ProviderType::Anthropic => "anthropic",
609            ProviderType::OpenRouter => "openrouter",
610            ProviderType::Local => "local",
611            ProviderType::Custom => "custom",
612        }
613    }
614
615    /// Parse from database string representation.
616    pub fn from_db_str(s: &str) -> Result<Self, EnumParseError> {
617        match s.to_lowercase().as_str() {
618            "openai" => Ok(ProviderType::OpenAI),
619            "anthropic" => Ok(ProviderType::Anthropic),
620            "openrouter" => Ok(ProviderType::OpenRouter),
621            "local" => Ok(ProviderType::Local),
622            "custom" => Ok(ProviderType::Custom),
623            _ => Err(EnumParseError {
624                enum_name: "ProviderType",
625                input: s.to_string(),
626            }),
627        }
628    }
629}
630
631impl fmt::Display for ProviderType {
632    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633        write!(f, "{}", self.as_db_str())
634    }
635}
636
637impl FromStr for ProviderType {
638    type Err = EnumParseError;
639
640    fn from_str(s: &str) -> Result<Self, Self::Err> {
641        Self::from_db_str(s)
642    }
643}
644
645/// Type of edge relationship between entities.
646#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
647#[serde(rename_all = "snake_case")]
648#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
649pub enum EdgeType {
650    Supports,
651    Contradicts,
652    Supersedes,
653    DerivedFrom,
654    RelatesTo,
655    Temporal,
656    Causal,
657    SynthesizedFrom,
658    Grouped,
659    Compared,
660}
661
662/// Semantic abstraction level for notes (L0 → L1 → L2 hierarchy).
663#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
664#[serde(rename_all = "snake_case")]
665#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
666pub enum AbstractionLevel {
667    #[default]
668    Raw,
669    Summary,
670    Principle,
671}
672
673/// Phase of pack config evolution cycle.
674#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
675#[serde(rename_all = "snake_case")]
676#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
677pub enum EvolutionPhase {
678    #[default]
679    Online,
680    Frozen,
681    Evolving,
682}
683
684/// Trigger condition for auto-summarization policy.
685#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
686#[serde(rename_all = "snake_case")]
687#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
688pub enum SummarizationTrigger {
689    DosageThreshold { percent: u8 },
690    ScopeClose,
691    TurnCount { count: i32 },
692    ArtifactCount { count: i32 },
693    Manual,
694}
695
696// ============================================================================
697// STRING CONVERSIONS
698// ============================================================================
699
700fn normalize_token(input: &str) -> String {
701    input
702        .chars()
703        .filter(|c| *c != '_')
704        .map(|c| c.to_ascii_lowercase())
705        .collect()
706}
707
708impl EntityType {
709    /// String form used in DB and wire (snake_case).
710    pub fn as_db_str(&self) -> &'static str {
711        match self {
712            EntityType::Trajectory => "trajectory",
713            EntityType::Scope => "scope",
714            EntityType::Artifact => "artifact",
715            EntityType::Note => "note",
716            EntityType::Turn => "turn",
717            EntityType::Tenant => "tenant",
718            EntityType::AgentWorkingSet => "agent_working_set",
719            EntityType::Link => "link",
720            EntityType::Belief => "belief",
721            EntityType::Goal => "goal",
722            EntityType::Plan => "plan",
723            EntityType::Lock => "lock",
724            EntityType::Message => "message",
725            EntityType::Agent => "agent",
726            EntityType::Delegation => "delegation",
727            EntityType::Handoff => "handoff",
728            EntityType::Config => "config",
729            EntityType::Conflict => "conflict",
730            EntityType::Edge => "edge",
731            EntityType::EvolutionSnapshot => "evolution_snapshot",
732            EntityType::SummarizationPolicy => "summarization_policy",
733            EntityType::SummarizationRequest => "summarization_request",
734            EntityType::ToolExecution => "tool_execution",
735            EntityType::Event => "event",
736        }
737    }
738}
739
740impl fmt::Display for EntityType {
741    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742        let value = match self {
743            EntityType::Trajectory => "Trajectory",
744            EntityType::Scope => "Scope",
745            EntityType::Artifact => "Artifact",
746            EntityType::Note => "Note",
747            EntityType::Turn => "Turn",
748            EntityType::Tenant => "Tenant",
749            EntityType::AgentWorkingSet => "AgentWorkingSet",
750            EntityType::Link => "Link",
751            EntityType::Belief => "Belief",
752            EntityType::Goal => "Goal",
753            EntityType::Plan => "Plan",
754            EntityType::Lock => "Lock",
755            EntityType::Message => "Message",
756            EntityType::Agent => "Agent",
757            EntityType::Delegation => "Delegation",
758            EntityType::Handoff => "Handoff",
759            EntityType::Config => "Config",
760            EntityType::Conflict => "Conflict",
761            EntityType::Edge => "Edge",
762            EntityType::EvolutionSnapshot => "EvolutionSnapshot",
763            EntityType::SummarizationPolicy => "SummarizationPolicy",
764            EntityType::SummarizationRequest => "SummarizationRequest",
765            EntityType::ToolExecution => "ToolExecution",
766            EntityType::Event => "Event",
767        };
768        write!(f, "{}", value)
769    }
770}
771
772impl FromStr for EntityType {
773    type Err = String;
774
775    fn from_str(s: &str) -> Result<Self, Self::Err> {
776        let normalized = normalize_token(s);
777        match normalized.as_str() {
778            "trajectory" => Ok(EntityType::Trajectory),
779            "scope" => Ok(EntityType::Scope),
780            "artifact" => Ok(EntityType::Artifact),
781            "note" => Ok(EntityType::Note),
782            "turn" => Ok(EntityType::Turn),
783            "tenant" => Ok(EntityType::Tenant),
784            "agentworkingset" => Ok(EntityType::AgentWorkingSet),
785            "link" => Ok(EntityType::Link),
786            "belief" => Ok(EntityType::Belief),
787            "goal" => Ok(EntityType::Goal),
788            "plan" => Ok(EntityType::Plan),
789            "lock" => Ok(EntityType::Lock),
790            "message" => Ok(EntityType::Message),
791            "agent" => Ok(EntityType::Agent),
792            "delegation" => Ok(EntityType::Delegation),
793            "handoff" => Ok(EntityType::Handoff),
794            "config" => Ok(EntityType::Config),
795            "conflict" => Ok(EntityType::Conflict),
796            "edge" => Ok(EntityType::Edge),
797            "evolutionsnapshot" => Ok(EntityType::EvolutionSnapshot),
798            "summarizationpolicy" => Ok(EntityType::SummarizationPolicy),
799            "summarizationrequest" => Ok(EntityType::SummarizationRequest),
800            "toolexecution" => Ok(EntityType::ToolExecution),
801            "event" => Ok(EntityType::Event),
802            _ => Err(format!("Invalid EntityType: {}", s)),
803        }
804    }
805}
806
807impl fmt::Display for TrajectoryStatus {
808    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
809        let value = match self {
810            TrajectoryStatus::Active => "active",
811            TrajectoryStatus::Completed => "completed",
812            TrajectoryStatus::Failed => "failed",
813            TrajectoryStatus::Suspended => "suspended",
814        };
815        write!(f, "{}", value)
816    }
817}
818
819impl FromStr for TrajectoryStatus {
820    type Err = String;
821
822    fn from_str(s: &str) -> Result<Self, Self::Err> {
823        match normalize_token(s).as_str() {
824            "active" => Ok(TrajectoryStatus::Active),
825            "completed" | "complete" => Ok(TrajectoryStatus::Completed),
826            "failed" | "failure" => Ok(TrajectoryStatus::Failed),
827            "suspended" => Ok(TrajectoryStatus::Suspended),
828            _ => Err(format!("Invalid TrajectoryStatus: {}", s)),
829        }
830    }
831}
832
833impl fmt::Display for OutcomeStatus {
834    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835        let value = match self {
836            OutcomeStatus::Success => "Success",
837            OutcomeStatus::Partial => "Partial",
838            OutcomeStatus::Failure => "Failure",
839        };
840        write!(f, "{}", value)
841    }
842}
843
844impl FromStr for OutcomeStatus {
845    type Err = String;
846
847    fn from_str(s: &str) -> Result<Self, Self::Err> {
848        match normalize_token(s).as_str() {
849            "success" => Ok(OutcomeStatus::Success),
850            "partial" => Ok(OutcomeStatus::Partial),
851            "failure" | "failed" => Ok(OutcomeStatus::Failure),
852            _ => Err(format!("Invalid OutcomeStatus: {}", s)),
853        }
854    }
855}
856
857impl fmt::Display for TurnRole {
858    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
859        let value = match self {
860            TurnRole::User => "user",
861            TurnRole::Assistant => "assistant",
862            TurnRole::System => "system",
863            TurnRole::Tool => "tool",
864        };
865        write!(f, "{}", value)
866    }
867}
868
869impl FromStr for TurnRole {
870    type Err = String;
871
872    fn from_str(s: &str) -> Result<Self, Self::Err> {
873        match normalize_token(s).as_str() {
874            "user" => Ok(TurnRole::User),
875            "assistant" => Ok(TurnRole::Assistant),
876            "system" => Ok(TurnRole::System),
877            "tool" => Ok(TurnRole::Tool),
878            _ => Err(format!("Invalid TurnRole: {}", s)),
879        }
880    }
881}
882
883impl fmt::Display for ArtifactType {
884    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
885        let value = match self {
886            ArtifactType::ErrorLog => "ErrorLog",
887            ArtifactType::CodePatch => "CodePatch",
888            ArtifactType::DesignDecision => "DesignDecision",
889            ArtifactType::UserPreference => "UserPreference",
890            ArtifactType::Fact => "Fact",
891            ArtifactType::Constraint => "Constraint",
892            ArtifactType::ToolResult => "ToolResult",
893            ArtifactType::IntermediateOutput => "IntermediateOutput",
894            ArtifactType::Custom => "Custom",
895            ArtifactType::Code => "Code",
896            ArtifactType::Document => "Document",
897            ArtifactType::Data => "Data",
898            ArtifactType::Model => "Model",
899            ArtifactType::Config => "Config",
900            ArtifactType::Log => "Log",
901            ArtifactType::Summary => "Summary",
902            ArtifactType::Decision => "Decision",
903            ArtifactType::Plan => "Plan",
904            ArtifactType::Audio => "Audio",
905            ArtifactType::Image => "Image",
906            ArtifactType::Video => "Video",
907            ArtifactType::Transcript => "Transcript",
908            ArtifactType::Screenshot => "Screenshot",
909        };
910        write!(f, "{}", value)
911    }
912}
913
914impl FromStr for ArtifactType {
915    type Err = String;
916
917    fn from_str(s: &str) -> Result<Self, Self::Err> {
918        match normalize_token(s).as_str() {
919            "errorlog" => Ok(ArtifactType::ErrorLog),
920            "codepatch" => Ok(ArtifactType::CodePatch),
921            "designdecision" => Ok(ArtifactType::DesignDecision),
922            "userpreference" => Ok(ArtifactType::UserPreference),
923            "fact" => Ok(ArtifactType::Fact),
924            "constraint" => Ok(ArtifactType::Constraint),
925            "toolresult" => Ok(ArtifactType::ToolResult),
926            "intermediateoutput" => Ok(ArtifactType::IntermediateOutput),
927            "custom" => Ok(ArtifactType::Custom),
928            "code" => Ok(ArtifactType::Code),
929            "document" => Ok(ArtifactType::Document),
930            "data" => Ok(ArtifactType::Data),
931            "model" => Ok(ArtifactType::Model),
932            "config" => Ok(ArtifactType::Config),
933            "log" => Ok(ArtifactType::Log),
934            "summary" => Ok(ArtifactType::Summary),
935            "decision" => Ok(ArtifactType::Decision),
936            "plan" => Ok(ArtifactType::Plan),
937            "audio" => Ok(ArtifactType::Audio),
938            "image" => Ok(ArtifactType::Image),
939            "video" => Ok(ArtifactType::Video),
940            "transcript" => Ok(ArtifactType::Transcript),
941            "screenshot" => Ok(ArtifactType::Screenshot),
942            _ => Err(format!("Invalid ArtifactType: {}", s)),
943        }
944    }
945}
946
947impl fmt::Display for ExtractionMethod {
948    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949        let value = match self {
950            ExtractionMethod::Explicit => "explicit",
951            ExtractionMethod::Inferred => "inferred",
952            ExtractionMethod::UserProvided => "user_provided",
953            ExtractionMethod::LlmExtraction => "llm_extraction",
954            ExtractionMethod::ToolExtraction => "tool_extraction",
955            ExtractionMethod::MemoryRecall => "memory_recall",
956            ExtractionMethod::ExternalApi => "external_api",
957            ExtractionMethod::Unknown => "unknown",
958        };
959        write!(f, "{}", value)
960    }
961}
962
963impl FromStr for ExtractionMethod {
964    type Err = String;
965
966    fn from_str(s: &str) -> Result<Self, Self::Err> {
967        match normalize_token(s).as_str() {
968            "explicit" => Ok(ExtractionMethod::Explicit),
969            "inferred" => Ok(ExtractionMethod::Inferred),
970            "userprovided" => Ok(ExtractionMethod::UserProvided),
971            "llmextraction" => Ok(ExtractionMethod::LlmExtraction),
972            "toolextraction" => Ok(ExtractionMethod::ToolExtraction),
973            "memoryrecall" => Ok(ExtractionMethod::MemoryRecall),
974            "externalapi" => Ok(ExtractionMethod::ExternalApi),
975            "unknown" => Ok(ExtractionMethod::Unknown),
976            _ => Err(format!("Invalid ExtractionMethod: {}", s)),
977        }
978    }
979}
980
981impl fmt::Display for NoteType {
982    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
983        let value = match self {
984            NoteType::Convention => "Convention",
985            NoteType::Strategy => "Strategy",
986            NoteType::Gotcha => "Gotcha",
987            NoteType::Fact => "Fact",
988            NoteType::Preference => "Preference",
989            NoteType::Relationship => "Relationship",
990            NoteType::Procedure => "Procedure",
991            NoteType::Meta => "Meta",
992            NoteType::Insight => "Insight",
993            NoteType::Correction => "Correction",
994            NoteType::Summary => "Summary",
995        };
996        write!(f, "{}", value)
997    }
998}
999
1000impl FromStr for NoteType {
1001    type Err = String;
1002
1003    fn from_str(s: &str) -> Result<Self, Self::Err> {
1004        match normalize_token(s).as_str() {
1005            "convention" => Ok(NoteType::Convention),
1006            "strategy" => Ok(NoteType::Strategy),
1007            "gotcha" => Ok(NoteType::Gotcha),
1008            "fact" => Ok(NoteType::Fact),
1009            "preference" => Ok(NoteType::Preference),
1010            "relationship" => Ok(NoteType::Relationship),
1011            "procedure" => Ok(NoteType::Procedure),
1012            "meta" => Ok(NoteType::Meta),
1013            "insight" => Ok(NoteType::Insight),
1014            "correction" => Ok(NoteType::Correction),
1015            "summary" => Ok(NoteType::Summary),
1016            _ => Err(format!("Invalid NoteType: {}", s)),
1017        }
1018    }
1019}
1020
1021impl fmt::Display for EdgeType {
1022    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1023        let value = match self {
1024            EdgeType::Supports => "supports",
1025            EdgeType::Contradicts => "contradicts",
1026            EdgeType::Supersedes => "supersedes",
1027            EdgeType::DerivedFrom => "derived_from",
1028            EdgeType::RelatesTo => "relates_to",
1029            EdgeType::Temporal => "temporal",
1030            EdgeType::Causal => "causal",
1031            EdgeType::SynthesizedFrom => "synthesized_from",
1032            EdgeType::Grouped => "grouped",
1033            EdgeType::Compared => "compared",
1034        };
1035        write!(f, "{}", value)
1036    }
1037}
1038
1039impl FromStr for EdgeType {
1040    type Err = String;
1041
1042    fn from_str(s: &str) -> Result<Self, Self::Err> {
1043        match normalize_token(s).as_str() {
1044            "supports" => Ok(EdgeType::Supports),
1045            "contradicts" => Ok(EdgeType::Contradicts),
1046            "supersedes" => Ok(EdgeType::Supersedes),
1047            "derivedfrom" => Ok(EdgeType::DerivedFrom),
1048            "relatesto" => Ok(EdgeType::RelatesTo),
1049            "temporal" => Ok(EdgeType::Temporal),
1050            "causal" => Ok(EdgeType::Causal),
1051            "synthesizedfrom" => Ok(EdgeType::SynthesizedFrom),
1052            "grouped" => Ok(EdgeType::Grouped),
1053            "compared" => Ok(EdgeType::Compared),
1054            _ => Err(format!("Invalid EdgeType: {}", s)),
1055        }
1056    }
1057}
1058
1059impl fmt::Display for AbstractionLevel {
1060    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1061        let value = match self {
1062            AbstractionLevel::Raw => "Raw",
1063            AbstractionLevel::Summary => "Summary",
1064            AbstractionLevel::Principle => "Principle",
1065        };
1066        write!(f, "{}", value)
1067    }
1068}
1069
1070impl FromStr for AbstractionLevel {
1071    type Err = String;
1072
1073    fn from_str(s: &str) -> Result<Self, Self::Err> {
1074        match normalize_token(s).as_str() {
1075            "raw" | "l0" => Ok(AbstractionLevel::Raw),
1076            "summary" | "l1" => Ok(AbstractionLevel::Summary),
1077            "principle" | "l2" => Ok(AbstractionLevel::Principle),
1078            _ => Err(format!("Invalid AbstractionLevel: {}", s)),
1079        }
1080    }
1081}
1082
1083impl fmt::Display for EvolutionPhase {
1084    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1085        let value = match self {
1086            EvolutionPhase::Online => "Online",
1087            EvolutionPhase::Frozen => "Frozen",
1088            EvolutionPhase::Evolving => "Evolving",
1089        };
1090        write!(f, "{}", value)
1091    }
1092}
1093
1094impl FromStr for EvolutionPhase {
1095    type Err = String;
1096
1097    fn from_str(s: &str) -> Result<Self, Self::Err> {
1098        match normalize_token(s).as_str() {
1099            "online" => Ok(EvolutionPhase::Online),
1100            "frozen" | "freeze" => Ok(EvolutionPhase::Frozen),
1101            "evolving" | "evolve" => Ok(EvolutionPhase::Evolving),
1102            _ => Err(format!("Invalid EvolutionPhase: {}", s)),
1103        }
1104    }
1105}
1106
1107impl fmt::Display for SummarizationTrigger {
1108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1109        match self {
1110            SummarizationTrigger::DosageThreshold { percent } => {
1111                write!(f, "DosageThreshold({}%)", percent)
1112            }
1113            SummarizationTrigger::ScopeClose => write!(f, "ScopeClose"),
1114            SummarizationTrigger::TurnCount { count } => write!(f, "TurnCount({})", count),
1115            SummarizationTrigger::ArtifactCount { count } => write!(f, "ArtifactCount({})", count),
1116            SummarizationTrigger::Manual => write!(f, "Manual"),
1117        }
1118    }
1119}
1120
1121// ============================================================================
1122// TESTS
1123// ============================================================================
1124
1125#[cfg(test)]
1126mod tests {
1127    use super::*;
1128
1129    // ========================================================================
1130    // Serde Roundtrip Tests - TTL
1131    // ========================================================================
1132
1133    #[test]
1134    fn test_ttl_persistent_serde_roundtrip() {
1135        let original = TTL::Persistent;
1136        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1137        let restored: TTL =
1138            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1139        assert_eq!(original, restored);
1140    }
1141
1142    #[test]
1143    fn test_ttl_session_serde_roundtrip() {
1144        let original = TTL::Session;
1145        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1146        let restored: TTL =
1147            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1148        assert_eq!(original, restored);
1149    }
1150
1151    #[test]
1152    fn test_ttl_scope_serde_roundtrip() {
1153        let original = TTL::Scope;
1154        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1155        let restored: TTL =
1156            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1157        assert_eq!(original, restored);
1158    }
1159
1160    #[test]
1161    fn test_ttl_duration_serde_roundtrip() {
1162        let original = TTL::Duration(DurationMs::new(3600000));
1163        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1164        let restored: TTL =
1165            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1166        assert_eq!(original, restored);
1167    }
1168
1169    #[test]
1170    fn test_ttl_ephemeral_serde_roundtrip() {
1171        let original = TTL::Ephemeral;
1172        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1173        let restored: TTL =
1174            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1175        assert_eq!(original, restored);
1176    }
1177
1178    #[test]
1179    fn test_ttl_max_serde_roundtrip() {
1180        let original = TTL::Max(1000);
1181        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1182        let restored: TTL =
1183            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1184        assert_eq!(original, restored);
1185    }
1186
1187    #[test]
1188    fn test_ttl_short_term_serde_roundtrip() {
1189        let original = TTL::ShortTerm;
1190        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1191        let restored: TTL =
1192            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1193        assert_eq!(original, restored);
1194    }
1195
1196    #[test]
1197    fn test_ttl_medium_term_serde_roundtrip() {
1198        let original = TTL::MediumTerm;
1199        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1200        let restored: TTL =
1201            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1202        assert_eq!(original, restored);
1203    }
1204
1205    #[test]
1206    fn test_ttl_long_term_serde_roundtrip() {
1207        let original = TTL::LongTerm;
1208        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1209        let restored: TTL =
1210            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1211        assert_eq!(original, restored);
1212    }
1213
1214    #[test]
1215    fn test_ttl_permanent_serde_roundtrip() {
1216        let original = TTL::Permanent;
1217        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1218        let restored: TTL =
1219            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1220        assert_eq!(original, restored);
1221    }
1222
1223    // ========================================================================
1224    // Serde Roundtrip Tests - EntityType
1225    // ========================================================================
1226
1227    #[test]
1228    fn test_entity_type_all_variants_serde_roundtrip() {
1229        let variants = [
1230            EntityType::Trajectory,
1231            EntityType::Scope,
1232            EntityType::Artifact,
1233            EntityType::Note,
1234            EntityType::Turn,
1235            EntityType::Tenant,
1236            EntityType::AgentWorkingSet,
1237            EntityType::Link,
1238            EntityType::Belief,
1239            EntityType::Goal,
1240            EntityType::Plan,
1241            EntityType::Lock,
1242            EntityType::Message,
1243            EntityType::Agent,
1244            EntityType::Delegation,
1245            EntityType::Handoff,
1246            EntityType::Config,
1247            EntityType::Conflict,
1248            EntityType::Edge,
1249            EntityType::EvolutionSnapshot,
1250            EntityType::SummarizationPolicy,
1251            EntityType::SummarizationRequest,
1252            EntityType::ToolExecution,
1253            EntityType::Event,
1254        ];
1255
1256        for original in variants {
1257            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1258            let restored: EntityType =
1259                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1260            assert_eq!(original, restored, "Failed for {:?}", original);
1261        }
1262    }
1263
1264    // ========================================================================
1265    // Serde Roundtrip Tests - TrajectoryStatus
1266    // ========================================================================
1267
1268    #[test]
1269    fn test_trajectory_status_all_variants_serde_roundtrip() {
1270        let variants = [
1271            TrajectoryStatus::Active,
1272            TrajectoryStatus::Completed,
1273            TrajectoryStatus::Failed,
1274            TrajectoryStatus::Suspended,
1275        ];
1276
1277        for original in variants {
1278            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1279            let restored: TrajectoryStatus =
1280                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1281            assert_eq!(original, restored);
1282        }
1283    }
1284
1285    // ========================================================================
1286    // Serde Roundtrip Tests - AgentStatus
1287    // ========================================================================
1288
1289    #[test]
1290    fn test_agent_status_all_variants_serde_roundtrip() {
1291        let variants = [
1292            AgentStatus::Idle,
1293            AgentStatus::Active,
1294            AgentStatus::Blocked,
1295            AgentStatus::Failed,
1296            AgentStatus::Offline,
1297        ];
1298
1299        for original in variants {
1300            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1301            let restored: AgentStatus =
1302                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1303            assert_eq!(original, restored);
1304        }
1305    }
1306
1307    // ========================================================================
1308    // Serde Roundtrip Tests - ArtifactType
1309    // ========================================================================
1310
1311    #[test]
1312    fn test_artifact_type_all_variants_serde_roundtrip() {
1313        let variants = [
1314            ArtifactType::ErrorLog,
1315            ArtifactType::CodePatch,
1316            ArtifactType::DesignDecision,
1317            ArtifactType::UserPreference,
1318            ArtifactType::Fact,
1319            ArtifactType::Constraint,
1320            ArtifactType::ToolResult,
1321            ArtifactType::IntermediateOutput,
1322            ArtifactType::Custom,
1323            ArtifactType::Code,
1324            ArtifactType::Document,
1325            ArtifactType::Data,
1326            ArtifactType::Model,
1327            ArtifactType::Config,
1328            ArtifactType::Log,
1329            ArtifactType::Summary,
1330            ArtifactType::Decision,
1331            ArtifactType::Plan,
1332        ];
1333
1334        for original in variants {
1335            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1336            let restored: ArtifactType =
1337                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1338            assert_eq!(original, restored);
1339        }
1340    }
1341
1342    // ========================================================================
1343    // Serde Roundtrip Tests - ExtractionMethod
1344    // ========================================================================
1345
1346    #[test]
1347    fn test_extraction_method_all_variants_serde_roundtrip() {
1348        let variants = [
1349            ExtractionMethod::Explicit,
1350            ExtractionMethod::Inferred,
1351            ExtractionMethod::UserProvided,
1352            ExtractionMethod::LlmExtraction,
1353            ExtractionMethod::ToolExtraction,
1354            ExtractionMethod::MemoryRecall,
1355            ExtractionMethod::ExternalApi,
1356            ExtractionMethod::Unknown,
1357        ];
1358
1359        for original in variants {
1360            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1361            let restored: ExtractionMethod =
1362                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1363            assert_eq!(original, restored);
1364        }
1365    }
1366
1367    // ========================================================================
1368    // Serde Roundtrip Tests - NoteType
1369    // ========================================================================
1370
1371    #[test]
1372    fn test_note_type_all_variants_serde_roundtrip() {
1373        let variants = [
1374            NoteType::Convention,
1375            NoteType::Strategy,
1376            NoteType::Gotcha,
1377            NoteType::Fact,
1378            NoteType::Preference,
1379            NoteType::Relationship,
1380            NoteType::Procedure,
1381            NoteType::Meta,
1382            NoteType::Insight,
1383            NoteType::Correction,
1384            NoteType::Summary,
1385        ];
1386
1387        for original in variants {
1388            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1389            let restored: NoteType =
1390                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1391            assert_eq!(original, restored);
1392        }
1393    }
1394
1395    // ========================================================================
1396    // Serde Roundtrip Tests - EdgeType
1397    // ========================================================================
1398
1399    #[test]
1400    fn test_edge_type_all_variants_serde_roundtrip() {
1401        let variants = [
1402            EdgeType::Supports,
1403            EdgeType::Contradicts,
1404            EdgeType::Supersedes,
1405            EdgeType::DerivedFrom,
1406            EdgeType::RelatesTo,
1407            EdgeType::Temporal,
1408            EdgeType::Causal,
1409            EdgeType::SynthesizedFrom,
1410            EdgeType::Grouped,
1411            EdgeType::Compared,
1412        ];
1413
1414        for original in variants {
1415            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1416            let restored: EdgeType =
1417                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1418            assert_eq!(original, restored);
1419        }
1420    }
1421
1422    // ========================================================================
1423    // Serde Roundtrip Tests - AbstractionLevel
1424    // ========================================================================
1425
1426    #[test]
1427    fn test_abstraction_level_all_variants_serde_roundtrip() {
1428        let variants = [
1429            AbstractionLevel::Raw,
1430            AbstractionLevel::Summary,
1431            AbstractionLevel::Principle,
1432        ];
1433
1434        for original in variants {
1435            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1436            let restored: AbstractionLevel =
1437                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1438            assert_eq!(original, restored);
1439        }
1440    }
1441
1442    // ========================================================================
1443    // Serde Roundtrip Tests - EvolutionPhase
1444    // ========================================================================
1445
1446    #[test]
1447    fn test_evolution_phase_all_variants_serde_roundtrip() {
1448        let variants = [
1449            EvolutionPhase::Online,
1450            EvolutionPhase::Frozen,
1451            EvolutionPhase::Evolving,
1452        ];
1453
1454        for original in variants {
1455            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1456            let restored: EvolutionPhase =
1457                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1458            assert_eq!(original, restored);
1459        }
1460    }
1461
1462    // ========================================================================
1463    // Serde Roundtrip Tests - SummarizationTrigger
1464    // ========================================================================
1465
1466    #[test]
1467    fn test_summarization_trigger_all_variants_serde_roundtrip() {
1468        let variants = [
1469            SummarizationTrigger::DosageThreshold { percent: 80 },
1470            SummarizationTrigger::ScopeClose,
1471            SummarizationTrigger::TurnCount { count: 10 },
1472            SummarizationTrigger::ArtifactCount { count: 5 },
1473            SummarizationTrigger::Manual,
1474        ];
1475
1476        for original in variants {
1477            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1478            let restored: SummarizationTrigger =
1479                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1480            assert_eq!(original, restored);
1481        }
1482    }
1483
1484    // ========================================================================
1485    // Serde Roundtrip Tests - MemoryCategory
1486    // ========================================================================
1487
1488    #[test]
1489    fn test_memory_category_all_variants_serde_roundtrip() {
1490        let variants = [
1491            MemoryCategory::Ephemeral,
1492            MemoryCategory::Working,
1493            MemoryCategory::Episodic,
1494            MemoryCategory::Semantic,
1495            MemoryCategory::Procedural,
1496            MemoryCategory::Meta,
1497        ];
1498
1499        for original in variants {
1500            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1501            let restored: MemoryCategory =
1502                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1503            assert_eq!(original, restored);
1504        }
1505    }
1506
1507    // ========================================================================
1508    // Serde Roundtrip Tests - TurnRole
1509    // ========================================================================
1510
1511    #[test]
1512    fn test_turn_role_all_variants_serde_roundtrip() {
1513        let variants = [
1514            TurnRole::User,
1515            TurnRole::Assistant,
1516            TurnRole::System,
1517            TurnRole::Tool,
1518        ];
1519
1520        for original in variants {
1521            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1522            let restored: TurnRole =
1523                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1524            assert_eq!(original, restored);
1525        }
1526    }
1527
1528    // ========================================================================
1529    // Serde Roundtrip Tests - OutcomeStatus
1530    // ========================================================================
1531
1532    #[test]
1533    fn test_outcome_status_all_variants_serde_roundtrip() {
1534        let variants = [
1535            OutcomeStatus::Success,
1536            OutcomeStatus::Partial,
1537            OutcomeStatus::Failure,
1538        ];
1539
1540        for original in variants {
1541            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1542            let restored: OutcomeStatus =
1543                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1544            assert_eq!(original, restored);
1545        }
1546    }
1547
1548    // ========================================================================
1549    // Serde Roundtrip Tests - FieldType
1550    // ========================================================================
1551
1552    #[test]
1553    fn test_field_type_simple_variants_serde_roundtrip() {
1554        let variants = [
1555            FieldType::Uuid,
1556            FieldType::Text,
1557            FieldType::Int,
1558            FieldType::Float,
1559            FieldType::Bool,
1560            FieldType::Timestamp,
1561            FieldType::Json,
1562        ];
1563
1564        for original in variants {
1565            let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1566            let restored: FieldType =
1567                serde_json::from_str(&json).expect("JSON deserialization should succeed");
1568            assert_eq!(original, restored);
1569        }
1570    }
1571
1572    #[test]
1573    fn test_field_type_embedding_serde_roundtrip() {
1574        let original = FieldType::Embedding {
1575            dimensions: Some(1536),
1576        };
1577        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1578        let restored: FieldType =
1579            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1580        assert_eq!(original, restored);
1581    }
1582
1583    #[test]
1584    fn test_field_type_enum_serde_roundtrip() {
1585        let original = FieldType::Enum {
1586            variants: vec!["A".to_string(), "B".to_string(), "C".to_string()],
1587        };
1588        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1589        let restored: FieldType =
1590            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1591        assert_eq!(original, restored);
1592    }
1593
1594    #[test]
1595    fn test_field_type_array_serde_roundtrip() {
1596        let original = FieldType::Array(Box::new(FieldType::Text));
1597        let json = serde_json::to_string(&original).expect("JSON serialization should succeed");
1598        let restored: FieldType =
1599            serde_json::from_str(&json).expect("JSON deserialization should succeed");
1600        assert_eq!(original, restored);
1601    }
1602
1603    // ========================================================================
1604    // Display/FromStr Roundtrip Tests
1605    // ========================================================================
1606
1607    #[test]
1608    fn test_entity_type_display_fromstr_roundtrip() {
1609        let variants = [
1610            EntityType::Trajectory,
1611            EntityType::Scope,
1612            EntityType::Artifact,
1613            EntityType::Note,
1614            EntityType::Turn,
1615            EntityType::Tenant,
1616            EntityType::AgentWorkingSet,
1617            EntityType::Link,
1618            EntityType::Belief,
1619            EntityType::Goal,
1620            EntityType::Plan,
1621            EntityType::Lock,
1622            EntityType::Message,
1623            EntityType::Agent,
1624            EntityType::Delegation,
1625            EntityType::Handoff,
1626            EntityType::Config,
1627            EntityType::Conflict,
1628            EntityType::Edge,
1629            EntityType::EvolutionSnapshot,
1630            EntityType::SummarizationPolicy,
1631            EntityType::SummarizationRequest,
1632            EntityType::ToolExecution,
1633            EntityType::Event,
1634        ];
1635
1636        for original in variants {
1637            let string = original.to_string();
1638            let restored: EntityType = string.parse().expect("EntityType roundtrip should succeed");
1639            assert_eq!(original, restored);
1640        }
1641    }
1642
1643    #[test]
1644    fn test_trajectory_status_display_fromstr_roundtrip() {
1645        let variants = [
1646            TrajectoryStatus::Active,
1647            TrajectoryStatus::Completed,
1648            TrajectoryStatus::Failed,
1649            TrajectoryStatus::Suspended,
1650        ];
1651
1652        for original in variants {
1653            let string = original.to_string();
1654            let restored: TrajectoryStatus = string
1655                .parse()
1656                .expect("TrajectoryStatus roundtrip should succeed");
1657            assert_eq!(original, restored);
1658        }
1659    }
1660
1661    #[test]
1662    fn test_agent_status_display_fromstr_roundtrip() {
1663        let variants = [
1664            AgentStatus::Idle,
1665            AgentStatus::Active,
1666            AgentStatus::Blocked,
1667            AgentStatus::Failed,
1668            AgentStatus::Offline,
1669        ];
1670
1671        for original in variants {
1672            let string = original.to_string();
1673            let restored: AgentStatus = string
1674                .parse()
1675                .expect("AgentStatus roundtrip should succeed");
1676            assert_eq!(original, restored);
1677        }
1678    }
1679
1680    #[test]
1681    fn test_artifact_type_display_fromstr_roundtrip() {
1682        let variants = [
1683            ArtifactType::ErrorLog,
1684            ArtifactType::CodePatch,
1685            ArtifactType::DesignDecision,
1686            ArtifactType::UserPreference,
1687            ArtifactType::Fact,
1688            ArtifactType::Constraint,
1689            ArtifactType::ToolResult,
1690            ArtifactType::IntermediateOutput,
1691            ArtifactType::Custom,
1692            ArtifactType::Code,
1693            ArtifactType::Document,
1694            ArtifactType::Data,
1695            ArtifactType::Model,
1696            ArtifactType::Config,
1697            ArtifactType::Log,
1698            ArtifactType::Summary,
1699            ArtifactType::Decision,
1700            ArtifactType::Plan,
1701        ];
1702
1703        for original in variants {
1704            let string = original.to_string();
1705            let restored: ArtifactType = string
1706                .parse()
1707                .expect("ArtifactType roundtrip should succeed");
1708            assert_eq!(original, restored);
1709        }
1710    }
1711
1712    #[test]
1713    fn test_extraction_method_display_fromstr_roundtrip() {
1714        let variants = [
1715            ExtractionMethod::Explicit,
1716            ExtractionMethod::Inferred,
1717            ExtractionMethod::UserProvided,
1718            ExtractionMethod::LlmExtraction,
1719            ExtractionMethod::ToolExtraction,
1720            ExtractionMethod::MemoryRecall,
1721            ExtractionMethod::ExternalApi,
1722            ExtractionMethod::Unknown,
1723        ];
1724
1725        for original in variants {
1726            let string = original.to_string();
1727            let restored: ExtractionMethod = string
1728                .parse()
1729                .expect("ExtractionMethod roundtrip should succeed");
1730            assert_eq!(original, restored);
1731        }
1732    }
1733
1734    #[test]
1735    fn test_note_type_display_fromstr_roundtrip() {
1736        let variants = [
1737            NoteType::Convention,
1738            NoteType::Strategy,
1739            NoteType::Gotcha,
1740            NoteType::Fact,
1741            NoteType::Preference,
1742            NoteType::Relationship,
1743            NoteType::Procedure,
1744            NoteType::Meta,
1745            NoteType::Insight,
1746            NoteType::Correction,
1747            NoteType::Summary,
1748        ];
1749
1750        for original in variants {
1751            let string = original.to_string();
1752            let restored: NoteType = string.parse().expect("NoteType roundtrip should succeed");
1753            assert_eq!(original, restored);
1754        }
1755    }
1756
1757    #[test]
1758    fn test_edge_type_display_fromstr_roundtrip() {
1759        let variants = [
1760            EdgeType::Supports,
1761            EdgeType::Contradicts,
1762            EdgeType::Supersedes,
1763            EdgeType::DerivedFrom,
1764            EdgeType::RelatesTo,
1765            EdgeType::Temporal,
1766            EdgeType::Causal,
1767            EdgeType::SynthesizedFrom,
1768            EdgeType::Grouped,
1769            EdgeType::Compared,
1770        ];
1771
1772        for original in variants {
1773            let string = original.to_string();
1774            let restored: EdgeType = string.parse().expect("EdgeType roundtrip should succeed");
1775            assert_eq!(original, restored);
1776        }
1777    }
1778
1779    #[test]
1780    fn test_abstraction_level_display_fromstr_roundtrip() {
1781        let variants = [
1782            AbstractionLevel::Raw,
1783            AbstractionLevel::Summary,
1784            AbstractionLevel::Principle,
1785        ];
1786
1787        for original in variants {
1788            let string = original.to_string();
1789            let restored: AbstractionLevel = string
1790                .parse()
1791                .expect("AbstractionLevel roundtrip should succeed");
1792            assert_eq!(original, restored);
1793        }
1794    }
1795
1796    #[test]
1797    fn test_evolution_phase_display_fromstr_roundtrip() {
1798        let variants = [
1799            EvolutionPhase::Online,
1800            EvolutionPhase::Frozen,
1801            EvolutionPhase::Evolving,
1802        ];
1803
1804        for original in variants {
1805            let string = original.to_string();
1806            let restored: EvolutionPhase = string
1807                .parse()
1808                .expect("EvolutionPhase roundtrip should succeed");
1809            assert_eq!(original, restored);
1810        }
1811    }
1812
1813    #[test]
1814    fn test_turn_role_display_fromstr_roundtrip() {
1815        let variants = [
1816            TurnRole::User,
1817            TurnRole::Assistant,
1818            TurnRole::System,
1819            TurnRole::Tool,
1820        ];
1821
1822        for original in variants {
1823            let string = original.to_string();
1824            let restored: TurnRole = string.parse().expect("TurnRole roundtrip should succeed");
1825            assert_eq!(original, restored);
1826        }
1827    }
1828
1829    #[test]
1830    fn test_outcome_status_display_fromstr_roundtrip() {
1831        let variants = [
1832            OutcomeStatus::Success,
1833            OutcomeStatus::Partial,
1834            OutcomeStatus::Failure,
1835        ];
1836
1837        for original in variants {
1838            let string = original.to_string();
1839            let restored: OutcomeStatus = string
1840                .parse()
1841                .expect("OutcomeStatus roundtrip should succeed");
1842            assert_eq!(original, restored);
1843        }
1844    }
1845
1846    // ========================================================================
1847    // FromStr with Aliases Tests
1848    // ========================================================================
1849
1850    #[test]
1851    fn test_abstraction_level_aliases() {
1852        // "raw" and "l0" should both parse to Raw
1853        assert_eq!(
1854            "raw"
1855                .parse::<AbstractionLevel>()
1856                .expect("parsing 'raw' should succeed"),
1857            AbstractionLevel::Raw
1858        );
1859        assert_eq!(
1860            "l0".parse::<AbstractionLevel>()
1861                .expect("parsing 'l0' should succeed"),
1862            AbstractionLevel::Raw
1863        );
1864
1865        // "summary" and "l1" should both parse to Summary
1866        assert_eq!(
1867            "summary"
1868                .parse::<AbstractionLevel>()
1869                .expect("parsing 'summary' should succeed"),
1870            AbstractionLevel::Summary
1871        );
1872        assert_eq!(
1873            "l1".parse::<AbstractionLevel>()
1874                .expect("parsing 'l1' should succeed"),
1875            AbstractionLevel::Summary
1876        );
1877
1878        // "principle" and "l2" should both parse to Principle
1879        assert_eq!(
1880            "principle"
1881                .parse::<AbstractionLevel>()
1882                .expect("parsing 'principle' should succeed"),
1883            AbstractionLevel::Principle
1884        );
1885        assert_eq!(
1886            "l2".parse::<AbstractionLevel>()
1887                .expect("parsing 'l2' should succeed"),
1888            AbstractionLevel::Principle
1889        );
1890    }
1891
1892    #[test]
1893    fn test_trajectory_status_aliases() {
1894        assert_eq!(
1895            "completed"
1896                .parse::<TrajectoryStatus>()
1897                .expect("parsing 'completed' should succeed"),
1898            TrajectoryStatus::Completed
1899        );
1900        assert_eq!(
1901            "complete"
1902                .parse::<TrajectoryStatus>()
1903                .expect("parsing 'complete' should succeed"),
1904            TrajectoryStatus::Completed
1905        );
1906        assert_eq!(
1907            "failed"
1908                .parse::<TrajectoryStatus>()
1909                .expect("parsing 'failed' should succeed"),
1910            TrajectoryStatus::Failed
1911        );
1912        assert_eq!(
1913            "failure"
1914                .parse::<TrajectoryStatus>()
1915                .expect("parsing 'failure' should succeed"),
1916            TrajectoryStatus::Failed
1917        );
1918    }
1919
1920    #[test]
1921    fn test_evolution_phase_aliases() {
1922        assert_eq!(
1923            "frozen"
1924                .parse::<EvolutionPhase>()
1925                .expect("parsing 'frozen' should succeed"),
1926            EvolutionPhase::Frozen
1927        );
1928        assert_eq!(
1929            "freeze"
1930                .parse::<EvolutionPhase>()
1931                .expect("parsing 'freeze' should succeed"),
1932            EvolutionPhase::Frozen
1933        );
1934        assert_eq!(
1935            "evolving"
1936                .parse::<EvolutionPhase>()
1937                .expect("parsing 'evolving' should succeed"),
1938            EvolutionPhase::Evolving
1939        );
1940        assert_eq!(
1941            "evolve"
1942                .parse::<EvolutionPhase>()
1943                .expect("parsing 'evolve' should succeed"),
1944            EvolutionPhase::Evolving
1945        );
1946    }
1947
1948    // ========================================================================
1949    // Default Implementation Tests
1950    // ========================================================================
1951
1952    #[test]
1953    fn test_agent_status_default() {
1954        assert_eq!(AgentStatus::default(), AgentStatus::Idle);
1955    }
1956
1957    #[test]
1958    fn test_extraction_method_default() {
1959        assert_eq!(ExtractionMethod::default(), ExtractionMethod::Unknown);
1960    }
1961
1962    #[test]
1963    fn test_abstraction_level_default() {
1964        assert_eq!(AbstractionLevel::default(), AbstractionLevel::Raw);
1965    }
1966
1967    #[test]
1968    fn test_evolution_phase_default() {
1969        assert_eq!(EvolutionPhase::default(), EvolutionPhase::Online);
1970    }
1971
1972    // ========================================================================
1973    // AgentStatus Business Logic Tests
1974    // ========================================================================
1975
1976    #[test]
1977    fn test_agent_status_can_accept_work() {
1978        assert!(AgentStatus::Idle.can_accept_work());
1979        assert!(!AgentStatus::Active.can_accept_work());
1980        assert!(!AgentStatus::Blocked.can_accept_work());
1981        assert!(!AgentStatus::Failed.can_accept_work());
1982        assert!(!AgentStatus::Offline.can_accept_work());
1983    }
1984
1985    #[test]
1986    fn test_agent_status_as_db_str() {
1987        assert_eq!(AgentStatus::Idle.as_db_str(), "idle");
1988        assert_eq!(AgentStatus::Active.as_db_str(), "active");
1989        assert_eq!(AgentStatus::Blocked.as_db_str(), "blocked");
1990        assert_eq!(AgentStatus::Failed.as_db_str(), "failed");
1991        assert_eq!(AgentStatus::Offline.as_db_str(), "offline");
1992    }
1993
1994    #[test]
1995    fn test_agent_status_from_db_str() {
1996        assert_eq!(
1997            AgentStatus::from_db_str("idle").expect("parsing 'idle' should succeed"),
1998            AgentStatus::Idle
1999        );
2000        assert_eq!(
2001            AgentStatus::from_db_str("ACTIVE").expect("parsing 'ACTIVE' should succeed"),
2002            AgentStatus::Active
2003        );
2004        assert_eq!(
2005            AgentStatus::from_db_str("Blocked").expect("parsing 'Blocked' should succeed"),
2006            AgentStatus::Blocked
2007        );
2008        assert!(AgentStatus::from_db_str("invalid").is_err());
2009    }
2010
2011    // ========================================================================
2012    // Invalid Parse Tests
2013    // ========================================================================
2014
2015    #[test]
2016    fn test_entity_type_invalid_parse() {
2017        assert!("invalid".parse::<EntityType>().is_err());
2018        assert!("".parse::<EntityType>().is_err());
2019    }
2020
2021    #[test]
2022    fn test_trajectory_status_invalid_parse() {
2023        assert!("invalid".parse::<TrajectoryStatus>().is_err());
2024    }
2025
2026    #[test]
2027    fn test_artifact_type_invalid_parse() {
2028        assert!("invalid".parse::<ArtifactType>().is_err());
2029    }
2030
2031    #[test]
2032    fn test_note_type_invalid_parse() {
2033        assert!("invalid".parse::<NoteType>().is_err());
2034    }
2035
2036    #[test]
2037    fn test_edge_type_invalid_parse() {
2038        assert!("invalid".parse::<EdgeType>().is_err());
2039    }
2040
2041    // ========================================================================
2042    // Case Insensitivity Tests
2043    // ========================================================================
2044
2045    #[test]
2046    fn test_parse_case_insensitive() {
2047        assert_eq!(
2048            "ACTIVE"
2049                .parse::<TrajectoryStatus>()
2050                .expect("parsing 'ACTIVE' should succeed"),
2051            TrajectoryStatus::Active
2052        );
2053        assert_eq!(
2054            "active"
2055                .parse::<TrajectoryStatus>()
2056                .expect("parsing 'active' should succeed"),
2057            TrajectoryStatus::Active
2058        );
2059        assert_eq!(
2060            "Active"
2061                .parse::<TrajectoryStatus>()
2062                .expect("parsing 'Active' should succeed"),
2063            TrajectoryStatus::Active
2064        );
2065        assert_eq!(
2066            "TRAJECTORY"
2067                .parse::<EntityType>()
2068                .expect("parsing 'TRAJECTORY' should succeed"),
2069            EntityType::Trajectory
2070        );
2071        assert_eq!(
2072            "trajectory"
2073                .parse::<EntityType>()
2074                .expect("parsing 'trajectory' should succeed"),
2075            EntityType::Trajectory
2076        );
2077    }
2078
2079    // ========================================================================
2080    // Error Type Tests
2081    // ========================================================================
2082
2083    #[test]
2084    fn test_enum_parse_error_display() {
2085        let err = EnumParseError {
2086            enum_name: "AgentStatus",
2087            input: "invalid_status".to_string(),
2088        };
2089        let msg = format!("{}", err);
2090        assert!(msg.contains("invalid_status"));
2091        assert!(msg.contains("AgentStatus"));
2092    }
2093
2094    // -----------------------------------------------------------------------
2095    // Moved from integration test: battle_intel_e2e.rs
2096    // -----------------------------------------------------------------------
2097
2098    #[test]
2099    fn battle_intel_edge_type_variants() {
2100        let all_edge_types = vec![
2101            EdgeType::Supports,
2102            EdgeType::Contradicts,
2103            EdgeType::Supersedes,
2104            EdgeType::DerivedFrom,
2105            EdgeType::RelatesTo,
2106            EdgeType::Temporal,
2107            EdgeType::Causal,
2108            EdgeType::SynthesizedFrom,
2109            EdgeType::Grouped,
2110            EdgeType::Compared,
2111        ];
2112
2113        for edge_type in &all_edge_types {
2114            let serialized =
2115                serde_json::to_string(edge_type).expect("EdgeType should serialize to JSON");
2116            let deserialized: EdgeType =
2117                serde_json::from_str(&serialized).expect("EdgeType should deserialize from JSON");
2118            assert_eq!(
2119                *edge_type, deserialized,
2120                "EdgeType {:?} round-trip failed. Serialized: {}",
2121                edge_type, serialized
2122            );
2123        }
2124
2125        for edge_type in &all_edge_types {
2126            match edge_type {
2127                EdgeType::Supports
2128                | EdgeType::Contradicts
2129                | EdgeType::Supersedes
2130                | EdgeType::DerivedFrom
2131                | EdgeType::RelatesTo
2132                | EdgeType::Temporal
2133                | EdgeType::Causal
2134                | EdgeType::SynthesizedFrom
2135                | EdgeType::Grouped
2136                | EdgeType::Compared => {}
2137            }
2138        }
2139
2140        let synth = EdgeType::SynthesizedFrom;
2141        let json = serde_json::to_value(synth).expect("should serialize");
2142        assert_eq!(json, serde_json::json!("synthesized_from"));
2143    }
2144
2145    #[test]
2146    fn battle_intel_summarization_request_status_transitions() {
2147        let statuses = vec![
2148            SummarizationRequestStatus::Pending,
2149            SummarizationRequestStatus::InProgress,
2150            SummarizationRequestStatus::Completed,
2151            SummarizationRequestStatus::Failed,
2152        ];
2153
2154        for status in &statuses {
2155            let json = serde_json::to_string(status).expect("status should serialize");
2156            let back: SummarizationRequestStatus =
2157                serde_json::from_str(&json).expect("status should deserialize");
2158            assert_eq!(*status, back, "Status round-trip failed for {:?}", status);
2159        }
2160
2161        assert_eq!(SummarizationRequestStatus::Pending.as_db_str(), "pending");
2162        assert_eq!(
2163            SummarizationRequestStatus::InProgress.as_db_str(),
2164            "in_progress"
2165        );
2166        assert_eq!(
2167            SummarizationRequestStatus::Completed.as_db_str(),
2168            "completed"
2169        );
2170        assert_eq!(SummarizationRequestStatus::Failed.as_db_str(), "failed");
2171    }
2172}