Skip to main content

agentic_comm/
lib.rs

1//! AgenticComm — agent-to-agent and agent-to-human communication engine.
2//!
3//! Provides structured messaging, channels, pub/sub, message routing, and
4//! communication history stored in `.acomm` files.
5
6use std::collections::HashMap;
7use std::fs::File;
8use std::io::{Read as IoRead, Write as IoWrite};
9use std::path::{Path, PathBuf};
10
11use chrono::{DateTime, Utc};
12use flate2::read::GzDecoder;
13use fs2::FileExt;
14use serde::{Deserialize, Serialize};
15use sha2::{Digest, Sha256};
16
17pub mod affect;
18pub mod bridges;
19pub mod cache;
20pub mod metrics;
21pub mod channel;
22pub mod crypto;
23pub mod encryption;
24pub mod format;
25pub mod query;
26pub mod semantic;
27pub mod temporal;
28pub mod types;
29pub mod workspace;
30pub mod contracts;
31
32pub use affect::*;
33pub use bridges::*;
34pub use channel::*;
35pub use crypto::*;
36pub use encryption::*;
37pub use format::*;
38pub use query::*;
39pub use semantic::*;
40pub use temporal::*;
41pub use types::*;
42pub use workspace::*;
43
44// ---------------------------------------------------------------------------
45// Errors
46// ---------------------------------------------------------------------------
47
48/// All errors produced by the communication engine.
49#[derive(thiserror::Error, Debug)]
50pub enum CommError {
51    /// Invalid channel name.
52    #[error("Invalid channel name: {0}")]
53    InvalidChannelName(String),
54
55    /// Invalid message content.
56    #[error("Invalid message content: {0}")]
57    InvalidContent(String),
58
59    /// Invalid sender.
60    #[error("Invalid sender: {0}")]
61    InvalidSender(String),
62
63    /// Channel not found.
64    #[error("Channel not found: {0}")]
65    ChannelNotFound(u64),
66
67    /// Message not found.
68    #[error("Message not found: {0}")]
69    MessageNotFound(u64),
70
71    /// Subscription not found.
72    #[error("Subscription not found: {0}")]
73    SubscriptionNotFound(u64),
74
75    /// Channel is full.
76    #[error("Channel {0} has reached maximum participants")]
77    ChannelFull(u64),
78
79    /// Participant not in channel.
80    #[error("Participant '{0}' is not in channel {1}")]
81    NotInChannel(String, u64),
82
83    /// Participant already in channel.
84    #[error("Participant '{0}' is already in channel {1}")]
85    AlreadyInChannel(String, u64),
86
87    /// Channel is in a state that does not allow the operation.
88    #[error("Channel {0} is {1} — operation not allowed")]
89    ChannelStateViolation(u64, String),
90
91    /// Dead letter index out of bounds.
92    #[error("Dead letter index {0} out of bounds")]
93    DeadLetterNotFound(usize),
94
95    /// I/O error.
96    #[error("IO error: {0}")]
97    Io(#[from] std::io::Error),
98
99    /// Serialization error.
100    #[error("Serialization error: {0}")]
101    Serialization(String),
102
103    /// Invalid file format.
104    #[error("Invalid .acomm file: {0}")]
105    InvalidFile(String),
106
107    /// Consent error.
108    #[error("Consent error: {0}")]
109    ConsentError(String),
110
111    /// Trust level error.
112    #[error("Trust error: {0}")]
113    TrustError(String),
114
115    /// Temporal scheduling error.
116    #[error("Temporal error: {0}")]
117    TemporalError(String),
118
119    /// Federation error.
120    #[error("Federation error: {0}")]
121    FederationError(String),
122
123    /// Hive mind error.
124    #[error("Hive error: {0}")]
125    HiveError(String),
126
127    /// Consent denied — the recipient has not granted the required consent.
128    #[error("Consent denied: {reason}")]
129    ConsentDenied { reason: String },
130
131    /// Rate limit exceeded — the sender has exceeded the configured rate limit.
132    #[error("Rate limit exceeded: {limit}")]
133    RateLimitExceeded { limit: String },
134
135    /// File locking error.
136    #[error("Lock error: {0}")]
137    LockError(String),
138
139    /// Key not found.
140    #[error("Key not found: {0}")]
141    KeyNotFound(u64),
142
143    /// Generic not-found error.
144    #[error("Not found: {0}")]
145    NotFound(String),
146}
147
148/// Convenience result type.
149pub type CommResult<T> = Result<T, CommError>;
150
151// ---------------------------------------------------------------------------
152// Core types
153// ---------------------------------------------------------------------------
154
155/// Maximum message content size: 1 MB.
156pub const MAX_CONTENT_SIZE: usize = 1_048_576;
157
158/// Magic bytes for the .acomm file format.
159pub const ACOMM_MAGIC: &[u8; 8] = b"ACOMM001";
160
161/// File format version.
162pub const ACOMM_VERSION: u32 = 1;
163
164/// The type of a message.
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166pub enum MessageType {
167    /// Plain text message.
168    Text,
169    /// A command to be executed.
170    Command,
171    /// A query expecting a response.
172    Query,
173    /// A response to a query.
174    Response,
175    /// A broadcast to all channel members.
176    Broadcast,
177    /// A system notification.
178    Notification,
179    /// Acknowledgment of receipt.
180    Acknowledgment,
181    /// An error message.
182    Error,
183}
184
185impl std::fmt::Display for MessageType {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        match self {
188            MessageType::Text => write!(f, "text"),
189            MessageType::Command => write!(f, "command"),
190            MessageType::Query => write!(f, "query"),
191            MessageType::Response => write!(f, "response"),
192            MessageType::Broadcast => write!(f, "broadcast"),
193            MessageType::Notification => write!(f, "notification"),
194            MessageType::Acknowledgment => write!(f, "acknowledgment"),
195            MessageType::Error => write!(f, "error"),
196        }
197    }
198}
199
200impl std::str::FromStr for MessageType {
201    type Err = String;
202    fn from_str(s: &str) -> Result<Self, Self::Err> {
203        match s.to_lowercase().as_str() {
204            "text" => Ok(MessageType::Text),
205            "command" => Ok(MessageType::Command),
206            "query" => Ok(MessageType::Query),
207            "response" => Ok(MessageType::Response),
208            "broadcast" => Ok(MessageType::Broadcast),
209            "notification" => Ok(MessageType::Notification),
210            "acknowledgment" | "ack" => Ok(MessageType::Acknowledgment),
211            "error" => Ok(MessageType::Error),
212            other => Err(format!("Unknown message type: {other}")),
213        }
214    }
215}
216
217// ---------------------------------------------------------------------------
218// MessageStatus
219// ---------------------------------------------------------------------------
220
221/// Lifecycle status of a message.
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
223pub enum MessageStatus {
224    /// Message has been created but not yet sent.
225    Created,
226    /// Message has been sent.
227    Sent,
228    /// Message has been delivered to the recipient.
229    Delivered,
230    /// Message has been read by the recipient.
231    Read,
232    /// Message has been acknowledged by the recipient.
233    Acknowledged,
234    /// Message sending failed.
235    Failed,
236    /// Message has expired (TTL exceeded).
237    Expired,
238    /// Message has been moved to the dead letter queue.
239    DeadLettered,
240}
241
242impl Default for MessageStatus {
243    fn default() -> Self {
244        MessageStatus::Created
245    }
246}
247
248impl std::fmt::Display for MessageStatus {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        match self {
251            MessageStatus::Created => write!(f, "created"),
252            MessageStatus::Sent => write!(f, "sent"),
253            MessageStatus::Delivered => write!(f, "delivered"),
254            MessageStatus::Read => write!(f, "read"),
255            MessageStatus::Acknowledged => write!(f, "acknowledged"),
256            MessageStatus::Failed => write!(f, "failed"),
257            MessageStatus::Expired => write!(f, "expired"),
258            MessageStatus::DeadLettered => write!(f, "dead_lettered"),
259        }
260    }
261}
262
263// ---------------------------------------------------------------------------
264// MessagePriority
265// ---------------------------------------------------------------------------
266
267/// Priority level for a message.
268#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
269pub enum MessagePriority {
270    /// Low priority.
271    Low = 0,
272    /// Normal priority (default).
273    Normal = 1,
274    /// High priority.
275    High = 2,
276    /// Urgent priority.
277    Urgent = 3,
278    /// Critical priority.
279    Critical = 4,
280}
281
282impl Default for MessagePriority {
283    fn default() -> Self {
284        MessagePriority::Normal
285    }
286}
287
288impl std::fmt::Display for MessagePriority {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        match self {
291            MessagePriority::Low => write!(f, "low"),
292            MessagePriority::Normal => write!(f, "normal"),
293            MessagePriority::High => write!(f, "high"),
294            MessagePriority::Urgent => write!(f, "urgent"),
295            MessagePriority::Critical => write!(f, "critical"),
296        }
297    }
298}
299
300// ---------------------------------------------------------------------------
301// ChannelState
302// ---------------------------------------------------------------------------
303
304/// Operational state of a channel.
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
306pub enum ChannelState {
307    /// Channel is active and fully operational.
308    Active,
309    /// Channel is paused — no new messages can be sent or received.
310    Paused,
311    /// Channel is draining — receives are allowed but sends are blocked.
312    Draining,
313    /// Channel is closed — all operations are blocked.
314    Closed,
315    /// Channel is archived — read-only but searchable.
316    Archived,
317    /// Shared semantic space without words.
318    SilentCommunion,
319    /// Merged consciousness state.
320    HiveMode,
321    /// Awaiting participant consent.
322    PendingConsent,
323}
324
325impl Default for ChannelState {
326    fn default() -> Self {
327        ChannelState::Active
328    }
329}
330
331impl std::fmt::Display for ChannelState {
332    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333        match self {
334            ChannelState::Active => write!(f, "active"),
335            ChannelState::Paused => write!(f, "paused"),
336            ChannelState::Draining => write!(f, "draining"),
337            ChannelState::Closed => write!(f, "closed"),
338            ChannelState::Archived => write!(f, "archived"),
339            ChannelState::SilentCommunion => write!(f, "silent_communion"),
340            ChannelState::HiveMode => write!(f, "hive_mode"),
341            ChannelState::PendingConsent => write!(f, "pending_consent"),
342        }
343    }
344}
345
346// ---------------------------------------------------------------------------
347// DeliveryMode
348// ---------------------------------------------------------------------------
349
350/// Message delivery semantics.
351#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
352pub enum DeliveryMode {
353    /// Message may be lost (fire and forget).
354    AtMostOnce,
355    /// Message will be delivered at least once (may duplicate).
356    AtLeastOnce,
357    /// Message will be delivered exactly once.
358    ExactlyOnce,
359}
360
361impl Default for DeliveryMode {
362    fn default() -> Self {
363        DeliveryMode::AtLeastOnce
364    }
365}
366
367// ---------------------------------------------------------------------------
368// RetentionPolicy
369// ---------------------------------------------------------------------------
370
371/// How long messages are retained in a channel.
372#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
373pub enum RetentionPolicy {
374    /// Messages are retained forever.
375    Forever,
376    /// Messages are retained for a given number of seconds.
377    Duration(u64),
378    /// Only the most recent N messages are retained.
379    MessageCount(u64),
380}
381
382impl Default for RetentionPolicy {
383    fn default() -> Self {
384        RetentionPolicy::Forever
385    }
386}
387
388// ---------------------------------------------------------------------------
389// DeadLetter
390// ---------------------------------------------------------------------------
391
392/// Reason a message was dead-lettered.
393#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
394pub enum DeadLetterReason {
395    /// The target channel was closed.
396    ChannelClosed,
397    /// The target channel was not found.
398    ChannelNotFound,
399    /// The intended recipient was unavailable.
400    RecipientUnavailable,
401    /// Maximum retry attempts were exceeded.
402    MaxRetriesExceeded,
403    /// The message expired (TTL exceeded).
404    Expired,
405    /// The message failed validation.
406    ValidationFailed(String),
407}
408
409impl std::fmt::Display for DeadLetterReason {
410    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411        match self {
412            DeadLetterReason::ChannelClosed => write!(f, "channel_closed"),
413            DeadLetterReason::ChannelNotFound => write!(f, "channel_not_found"),
414            DeadLetterReason::RecipientUnavailable => write!(f, "recipient_unavailable"),
415            DeadLetterReason::MaxRetriesExceeded => write!(f, "max_retries_exceeded"),
416            DeadLetterReason::Expired => write!(f, "expired"),
417            DeadLetterReason::ValidationFailed(s) => write!(f, "validation_failed: {s}"),
418        }
419    }
420}
421
422/// A message that could not be delivered and was placed in the dead letter queue.
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct DeadLetter {
425    /// The original message that failed delivery.
426    pub original_message: Message,
427    /// Why the message was dead-lettered.
428    pub reason: DeadLetterReason,
429    /// When the message was dead-lettered.
430    pub dead_lettered_at: DateTime<Utc>,
431    /// Number of delivery retries attempted.
432    pub retry_count: u32,
433}
434
435// ---------------------------------------------------------------------------
436// Key management
437// ---------------------------------------------------------------------------
438
439/// A key entry for channel encryption metadata.
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct KeyEntry {
442    /// Unique key identifier.
443    pub id: u64,
444    /// Algorithm name (e.g. "aes-256-gcm", "x25519").
445    pub algorithm: String,
446    /// Creation timestamp (seconds since epoch).
447    pub created_at: u64,
448    /// Key status: "active", "rotated", or "revoked".
449    pub status: String,
450    /// Optional channel this key is bound to.
451    pub channel_id: Option<u64>,
452    /// Fingerprint of the key material.
453    pub fingerprint: String,
454}
455
456// ---------------------------------------------------------------------------
457// Message
458// ---------------------------------------------------------------------------
459
460/// A single message in the communication system.
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct Message {
463    /// Unique message identifier.
464    pub id: u64,
465    /// Channel this message belongs to.
466    pub channel_id: u64,
467    /// Who sent the message.
468    pub sender: String,
469    /// Optional specific recipient (None = all channel participants).
470    pub recipient: Option<String>,
471    /// Message body.
472    pub content: String,
473    /// Type of message.
474    pub message_type: MessageType,
475    /// When the message was created (UTC).
476    pub timestamp: DateTime<Utc>,
477    /// Arbitrary key-value metadata.
478    pub metadata: HashMap<String, String>,
479    /// Optional SHA-256 content signature.
480    pub signature: Option<String>,
481    /// Set of participants who have acknowledged this message.
482    #[serde(default)]
483    pub acknowledged_by: Vec<String>,
484    /// Lifecycle status of this message.
485    #[serde(default)]
486    pub status: MessageStatus,
487    /// Priority level of this message.
488    #[serde(default)]
489    pub priority: MessagePriority,
490    /// ID of the message this is a reply to.
491    #[serde(default)]
492    pub reply_to: Option<u64>,
493    /// Correlation ID for request/response pairing.
494    #[serde(default)]
495    pub correlation_id: Option<String>,
496    /// Thread grouping identifier.
497    #[serde(default)]
498    pub thread_id: Option<String>,
499    /// Causal timestamp with Lamport/vector clocks.
500    #[serde(default)]
501    pub comm_timestamp: CommTimestamp,
502    /// Rich message content stored as JSON (avoids bincode enum issues).
503    /// Use `MessageContent::from_json_string` / `to_json_string` to convert.
504    #[serde(default)]
505    pub rich_content_json: Option<String>,
506    /// UUID-based universal identifier (alongside legacy u64 id).
507    #[serde(default)]
508    pub comm_id: Option<CommId>,
509    /// Identity receipt ID for this message (from agentic-identity).
510    #[serde(default)]
511    pub receipt_id: Option<String>,
512}
513
514/// The type of a communication channel.
515#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
516pub enum ChannelType {
517    /// 1:1 direct message.
518    Direct,
519    /// Group conversation.
520    Group,
521    /// One-to-many broadcast.
522    Broadcast,
523    /// Publish/subscribe topic.
524    PubSub,
525    /// Shared state space channel.
526    Telepathic,
527    /// Hive mind channel.
528    Hive,
529    /// Time-shifted messaging.
530    Temporal,
531    /// Fate-linked communication.
532    Destiny,
533    /// Predictive messaging.
534    Oracle,
535}
536
537impl std::fmt::Display for ChannelType {
538    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539        match self {
540            ChannelType::Direct => write!(f, "direct"),
541            ChannelType::Group => write!(f, "group"),
542            ChannelType::Broadcast => write!(f, "broadcast"),
543            ChannelType::PubSub => write!(f, "pubsub"),
544            ChannelType::Telepathic => write!(f, "telepathic"),
545            ChannelType::Hive => write!(f, "hive"),
546            ChannelType::Temporal => write!(f, "temporal"),
547            ChannelType::Destiny => write!(f, "destiny"),
548            ChannelType::Oracle => write!(f, "oracle"),
549        }
550    }
551}
552
553impl std::str::FromStr for ChannelType {
554    type Err = String;
555    fn from_str(s: &str) -> Result<Self, Self::Err> {
556        match s.to_lowercase().as_str() {
557            "direct" => Ok(ChannelType::Direct),
558            "group" => Ok(ChannelType::Group),
559            "broadcast" => Ok(ChannelType::Broadcast),
560            "pubsub" => Ok(ChannelType::PubSub),
561            "telepathic" => Ok(ChannelType::Telepathic),
562            "hive" => Ok(ChannelType::Hive),
563            "temporal" => Ok(ChannelType::Temporal),
564            "destiny" => Ok(ChannelType::Destiny),
565            "oracle" => Ok(ChannelType::Oracle),
566            other => Err(format!("Unknown channel type: {other}")),
567        }
568    }
569}
570
571/// Configuration for a channel.
572#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct ChannelConfig {
574    /// Maximum number of participants (0 = unlimited).
575    pub max_participants: u32,
576    /// Message time-to-live in seconds (0 = forever).
577    pub ttl_seconds: u64,
578    /// Whether messages should be persisted.
579    pub persistence: bool,
580    /// Whether encryption is required for this channel.
581    pub encryption_required: bool,
582    /// Message delivery semantics.
583    #[serde(default)]
584    pub delivery_mode: DeliveryMode,
585    /// How long messages are retained.
586    #[serde(default)]
587    pub retention_policy: RetentionPolicy,
588    /// Minimum trust level required to send messages or join this channel.
589    /// If `None`, no trust check is enforced.
590    #[serde(default)]
591    pub min_trust_level: Option<CommTrustLevel>,
592}
593
594impl Default for ChannelConfig {
595    fn default() -> Self {
596        Self {
597            max_participants: 0,
598            ttl_seconds: 0,
599            persistence: true,
600            encryption_required: false,
601            delivery_mode: DeliveryMode::default(),
602            retention_policy: RetentionPolicy::default(),
603            min_trust_level: None,
604        }
605    }
606}
607
608/// A communication channel.
609#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct Channel {
611    /// Unique channel identifier.
612    pub id: u64,
613    /// Human-readable channel name.
614    pub name: String,
615    /// Type of channel.
616    pub channel_type: ChannelType,
617    /// When the channel was created.
618    pub created_at: DateTime<Utc>,
619    /// Current participants.
620    pub participants: Vec<String>,
621    /// Channel configuration.
622    pub config: ChannelConfig,
623    /// Operational state of the channel.
624    #[serde(default)]
625    pub state: ChannelState,
626    /// UUID-based universal identifier (alongside legacy u64 id).
627    #[serde(default)]
628    pub comm_id: Option<CommId>,
629    /// Optional contract reference for SLA enforcement.
630    #[serde(default)]
631    pub contract_ref: Option<String>,
632}
633
634/// A pub/sub subscription.
635#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct Subscription {
637    /// Unique subscription identifier.
638    pub id: u64,
639    /// Topic being subscribed to.
640    pub topic: String,
641    /// Who is subscribed.
642    pub subscriber: String,
643    /// When the subscription was created.
644    pub created_at: DateTime<Utc>,
645}
646
647/// Filter for querying message history.
648#[derive(Debug, Clone, Default, Serialize, Deserialize)]
649pub struct MessageFilter {
650    /// Only messages after this time.
651    pub since: Option<DateTime<Utc>>,
652    /// Only messages before this time.
653    pub before: Option<DateTime<Utc>>,
654    /// Only messages from this sender.
655    pub sender: Option<String>,
656    /// Only messages of this type.
657    pub message_type: Option<MessageType>,
658    /// Maximum number of results.
659    pub limit: Option<usize>,
660    /// Filter by message priority (numeric: 0=Low, 1=Normal, 2=High, 3=Urgent, 4=Critical).
661    #[serde(default)]
662    pub priority: Option<u32>,
663    /// Filter by thread identifier.
664    #[serde(default)]
665    pub thread_id: Option<u64>,
666    /// Filter by content substring (case-insensitive).
667    #[serde(default)]
668    pub content_contains: Option<String>,
669}
670
671/// File header for .acomm files.
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct AcommHeader {
674    /// Magic bytes (always "ACOMM001").
675    pub magic: [u8; 8],
676    /// Format version.
677    pub version: u32,
678    /// Number of channels in the file.
679    pub channel_count: u32,
680    /// Number of messages in the file.
681    pub message_count: u64,
682}
683
684// ---------------------------------------------------------------------------
685// CommStore — the main store
686// ---------------------------------------------------------------------------
687
688/// Per-sender rate tracking state.
689#[derive(Debug, Clone, Default)]
690pub struct RateTracker {
691    /// Number of messages sent in the current minute window.
692    pub message_count_minute: u32,
693    /// Epoch second when the minute window was last reset.
694    pub last_minute_reset: u64,
695    /// Number of messages sent in the current hour window.
696    pub message_count_hour: u32,
697    /// Epoch second when the hour window was last reset.
698    pub last_hour_reset: u64,
699}
700
701// ---------------------------------------------------------------------------
702// File locking
703// ---------------------------------------------------------------------------
704
705/// Advisory file lock for concurrent access to `.acomm` files.
706///
707/// Uses `fs2` advisory locks so that multiple processes can safely read/write
708/// the same `.acomm` file without corruption.  The sidecar lock file lives at
709/// `<data_path>.acomm.lock`.
710pub struct CommFileLock {
711    lock_file: File,
712    lock_path: PathBuf,
713}
714
715impl CommFileLock {
716    /// Acquire an exclusive lock on the `.acomm` file (blocks until available).
717    pub fn acquire(data_path: &Path) -> CommResult<Self> {
718        let lock_path = data_path.with_extension("acomm.lock");
719        let lock_file = File::create(&lock_path).map_err(|e| {
720            CommError::LockError(format!("Failed to create lock file: {}", e))
721        })?;
722        lock_file.lock_exclusive().map_err(|e| {
723            CommError::LockError(format!("Failed to acquire exclusive lock: {}", e))
724        })?;
725        Ok(Self { lock_file, lock_path })
726    }
727
728    /// Try to acquire an exclusive lock without blocking.
729    ///
730    /// Returns an error immediately if the lock is already held by another
731    /// process.
732    pub fn try_acquire(data_path: &Path) -> CommResult<Self> {
733        let lock_path = data_path.with_extension("acomm.lock");
734        let lock_file = File::create(&lock_path).map_err(|e| {
735            CommError::LockError(format!("Failed to create lock file: {}", e))
736        })?;
737        lock_file.try_lock_exclusive().map_err(|e| {
738            CommError::LockError(format!("Failed to acquire lock (already held): {}", e))
739        })?;
740        Ok(Self { lock_file, lock_path })
741    }
742
743    /// Acquire a shared (read) lock on the `.acomm` file (blocks until
744    /// available).
745    ///
746    /// Multiple readers can hold a shared lock simultaneously, but a shared
747    /// lock blocks exclusive writers.
748    pub fn acquire_shared(data_path: &Path) -> CommResult<Self> {
749        let lock_path = data_path.with_extension("acomm.lock");
750        let lock_file = File::create(&lock_path).map_err(|e| {
751            CommError::LockError(format!("Failed to create lock file: {}", e))
752        })?;
753        lock_file.lock_shared().map_err(|e| {
754            CommError::LockError(format!("Failed to acquire shared lock: {}", e))
755        })?;
756        Ok(Self { lock_file, lock_path })
757    }
758
759    /// Explicitly release the lock and attempt to clean up the lock file.
760    pub fn release(self) -> CommResult<()> {
761        self.lock_file.unlock().map_err(|e| {
762            CommError::LockError(format!("Failed to release lock: {}", e))
763        })?;
764        // Best-effort cleanup of the sidecar file.
765        let _ = std::fs::remove_file(&self.lock_path);
766        Ok(())
767    }
768
769    /// Check if the lock file is stale (older than `max_age_secs` seconds) and
770    /// remove it if so.
771    ///
772    /// Returns `Ok(true)` if a stale lock was recovered, `Ok(false)` otherwise.
773    pub fn recover_stale(data_path: &Path, max_age_secs: u64) -> CommResult<bool> {
774        let lock_path = data_path.with_extension("acomm.lock");
775        if lock_path.exists() {
776            if let Ok(metadata) = std::fs::metadata(&lock_path) {
777                if let Ok(modified) = metadata.modified() {
778                    if let Ok(elapsed) = modified.elapsed() {
779                        if elapsed.as_secs() > max_age_secs {
780                            let _ = std::fs::remove_file(&lock_path);
781                            return Ok(true);
782                        }
783                    }
784                }
785            }
786        }
787        Ok(false)
788    }
789}
790
791impl Drop for CommFileLock {
792    fn drop(&mut self) {
793        let _ = self.lock_file.unlock();
794        let _ = std::fs::remove_file(&self.lock_path);
795    }
796}
797
798// ---------------------------------------------------------------------------
799// CommStore
800// ---------------------------------------------------------------------------
801
802/// The main communication store holding channels, messages, and subscriptions.
803#[derive(Debug, Clone, Serialize, Deserialize)]
804pub struct CommStore {
805    /// All channels, keyed by channel id.
806    pub channels: HashMap<u64, Channel>,
807    /// All messages, keyed by message id.
808    pub messages: HashMap<u64, Message>,
809    /// All subscriptions, keyed by subscription id.
810    pub subscriptions: HashMap<u64, Subscription>,
811    /// Next channel id.
812    next_channel_id: u64,
813    /// Next message id.
814    next_message_id: u64,
815    /// Next subscription id.
816    next_subscription_id: u64,
817    /// Dead letter queue for undeliverable messages.
818    #[serde(default)]
819    pub dead_letters: Vec<DeadLetter>,
820
821    /// Consent gates: (grantor, grantee, scope) -> ConsentGateEntry.
822    #[serde(default)]
823    pub consent_gates: Vec<ConsentGateEntry>,
824
825    /// Trust level overrides: agent_id -> trust_level.
826    #[serde(default)]
827    pub trust_levels: HashMap<String, CommTrustLevel>,
828
829    /// Temporal message queue.
830    #[serde(default)]
831    pub temporal_queue: Vec<TemporalMessage>,
832
833    /// Next temporal message ID.
834    #[serde(default = "default_one")]
835    next_temporal_id: u64,
836
837    /// Federation configuration.
838    #[serde(default)]
839    pub federation_config: FederationConfig,
840
841    /// Hive minds.
842    #[serde(default)]
843    pub hive_minds: HashMap<u64, HiveMind>,
844
845    /// Next hive mind ID.
846    #[serde(default = "default_one")]
847    next_hive_id: u64,
848
849    /// Communication log entries.
850    #[serde(default)]
851    pub comm_log: Vec<CommunicationLogEntry>,
852
853    /// Next log entry index.
854    #[serde(default = "default_one")]
855    next_log_index: u64,
856
857    /// Audit log entries.
858    #[serde(default)]
859    pub audit_log: Vec<AuditEntry>,
860
861    /// Rate limit configuration.
862    #[serde(default)]
863    pub rate_limit_config: RateLimitConfig,
864
865    /// Semantic operations log.
866    #[serde(default)]
867    pub semantic_operations: Vec<SemanticOperation>,
868
869    /// Next semantic operation ID.
870    #[serde(default = "default_one")]
871    next_semantic_id: u64,
872
873    /// Semantic conflicts.
874    #[serde(default)]
875    pub semantic_conflicts: Vec<SemanticConflict>,
876
877    /// Per-agent affect states.
878    #[serde(default)]
879    pub affect_states: HashMap<String, AffectState>,
880
881    /// Affect contagion resistance (global default).
882    #[serde(default = "default_resistance")]
883    pub affect_resistance: f64,
884
885    /// Pending consent requests.
886    #[serde(default)]
887    pub pending_consent_requests: Vec<ConsentRequest>,
888
889    /// Meld sessions.
890    #[serde(default)]
891    pub meld_sessions: Vec<MeldSession>,
892
893    /// Per-zone federation policies.
894    #[serde(default)]
895    pub zone_policies: HashMap<String, ZonePolicyConfig>,
896
897    /// Key metadata for channel encryption.
898    #[serde(default)]
899    pub key_store: Vec<KeyEntry>,
900
901    /// Next key ID.
902    #[serde(default = "default_one")]
903    next_key_id: u64,
904
905    /// Global Lamport counter for causal ordering of messages.
906    #[serde(default)]
907    pub lamport_counter: u64,
908
909    /// Per-sender rate tracking (not persisted — rebuilt at runtime).
910    #[serde(skip)]
911    pub rate_trackers: HashMap<String, RateTracker>,
912
913    /// Optional Ed25519 key pair for cryptographic message signing.
914    /// Not serialized — must be set at runtime via `set_signing_key`.
915    #[serde(skip)]
916    pub key_pair: Option<CommKeyPair>,
917
918    /// Registered communicating agents (agent_id -> CommunicatingAgent).
919    #[serde(default)]
920    pub agents: HashMap<String, CommunicatingAgent>,
921
922    /// Bridge configuration for sister integrations.
923    /// Not serialized — must be set at runtime via `set_bridge_config`.
924    #[serde(skip)]
925    pub bridge_config: BridgeConfig,
926
927    /// Embedding vectors for semantic search, keyed by message ID.
928    ///
929    /// Each entry maps a message ID to its embedding vector (e.g. from an
930    /// external model). Supports brute-force cosine similarity search.
931    #[serde(default)]
932    pub embeddings: HashMap<u64, Vec<f32>>,
933}
934
935fn default_one() -> u64 {
936    1
937}
938
939fn default_resistance() -> f64 {
940    0.5
941}
942
943impl Default for CommStore {
944    fn default() -> Self {
945        Self::new()
946    }
947}
948
949impl CommStore {
950    /// Create a new empty communication store.
951    pub fn new() -> Self {
952        Self {
953            channels: HashMap::new(),
954            messages: HashMap::new(),
955            subscriptions: HashMap::new(),
956            next_channel_id: 1,
957            next_message_id: 1,
958            next_subscription_id: 1,
959            dead_letters: Vec::new(),
960            consent_gates: Vec::new(),
961            trust_levels: HashMap::new(),
962            temporal_queue: Vec::new(),
963            next_temporal_id: 1,
964            federation_config: FederationConfig::default(),
965            hive_minds: HashMap::new(),
966            next_hive_id: 1,
967            comm_log: Vec::new(),
968            next_log_index: 1,
969            audit_log: Vec::new(),
970            rate_limit_config: RateLimitConfig::default(),
971            semantic_operations: Vec::new(),
972            next_semantic_id: 1,
973            semantic_conflicts: Vec::new(),
974            affect_states: HashMap::new(),
975            affect_resistance: 0.5,
976            pending_consent_requests: Vec::new(),
977            meld_sessions: Vec::new(),
978            zone_policies: HashMap::new(),
979            key_store: Vec::new(),
980            next_key_id: 1,
981            lamport_counter: 0,
982            rate_trackers: HashMap::new(),
983            key_pair: None,
984            agents: HashMap::new(),
985            bridge_config: BridgeConfig::default(),
986            embeddings: HashMap::new(),
987        }
988    }
989
990    // -----------------------------------------------------------------------
991    // Bridge configuration
992    // -----------------------------------------------------------------------
993
994    /// Set the bridge configuration for sister integrations.
995    pub fn set_bridge_config(&mut self, config: BridgeConfig) {
996        self.bridge_config = config;
997    }
998
999    // -----------------------------------------------------------------------
1000    // Agent registry
1001    // -----------------------------------------------------------------------
1002
1003    /// Register a new communicating agent.
1004    pub fn register_agent(&mut self, agent: CommunicatingAgent) -> CommResult<()> {
1005        let agent_id = agent.agent_id.clone();
1006        self.agents.insert(agent_id.clone(), agent);
1007        self.log_audit(
1008            AuditEventType::AgentRegistered,
1009            &agent_id,
1010            &format!("Agent registered: {}", agent_id),
1011            Some(agent_id.clone()),
1012        );
1013        Ok(())
1014    }
1015
1016    /// Get a registered agent by ID.
1017    pub fn get_agent(&self, agent_id: &str) -> Option<&CommunicatingAgent> {
1018        self.agents.get(agent_id)
1019    }
1020
1021    /// List all registered agents.
1022    pub fn list_agents(&self) -> Vec<&CommunicatingAgent> {
1023        self.agents.values().collect()
1024    }
1025
1026    /// Update agent availability/presence.
1027    pub fn update_agent_availability(
1028        &mut self,
1029        agent_id: &str,
1030        availability: Availability,
1031    ) -> CommResult<()> {
1032        if let Some(agent) = self.agents.get_mut(agent_id) {
1033            agent.availability = availability;
1034            Ok(())
1035        } else {
1036            Err(CommError::NotFound(format!("Agent not found: {}", agent_id)))
1037        }
1038    }
1039
1040    /// Remove a registered agent.
1041    pub fn unregister_agent(&mut self, agent_id: &str) -> CommResult<()> {
1042        if self.agents.remove(agent_id).is_some() {
1043            self.log_audit(
1044                AuditEventType::AgentUnregistered,
1045                agent_id,
1046                &format!("Agent unregistered: {}", agent_id),
1047                Some(agent_id.to_string()),
1048            );
1049            Ok(())
1050        } else {
1051            Err(CommError::NotFound(format!("Agent not found: {}", agent_id)))
1052        }
1053    }
1054
1055    // -----------------------------------------------------------------------
1056    // Validation helpers
1057    // -----------------------------------------------------------------------
1058
1059    fn validate_channel_name(name: &str) -> CommResult<()> {
1060        if name.is_empty() || name.len() > 128 {
1061            return Err(CommError::InvalidChannelName(
1062                "Channel name must be 1-128 characters".to_string(),
1063            ));
1064        }
1065        if !name
1066            .chars()
1067            .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
1068        {
1069            return Err(CommError::InvalidChannelName(
1070                "Channel name must contain only alphanumeric characters, hyphens, or underscores"
1071                    .to_string(),
1072            ));
1073        }
1074        Ok(())
1075    }
1076
1077    fn validate_content(content: &str) -> CommResult<()> {
1078        if content.is_empty() {
1079            return Err(CommError::InvalidContent(
1080                "Message content cannot be empty".to_string(),
1081            ));
1082        }
1083        if content.len() > MAX_CONTENT_SIZE {
1084            return Err(CommError::InvalidContent(format!(
1085                "Message content exceeds maximum size of {} bytes",
1086                MAX_CONTENT_SIZE
1087            )));
1088        }
1089        Ok(())
1090    }
1091
1092    fn validate_sender(sender: &str) -> CommResult<()> {
1093        if sender.is_empty() {
1094            return Err(CommError::InvalidSender(
1095                "Sender cannot be empty".to_string(),
1096            ));
1097        }
1098        Ok(())
1099    }
1100
1101    /// Compute a SHA-256 hash signature (legacy, used as fallback).
1102    fn compute_sha256_signature(content: &str) -> String {
1103        let mut hasher = Sha256::new();
1104        hasher.update(content.as_bytes());
1105        format!("{:x}", hasher.finalize())
1106    }
1107
1108    /// Compute a message signature.
1109    ///
1110    /// When an Ed25519 key pair is set, produces a real cryptographic
1111    /// signature (128 hex chars / 64 bytes). Otherwise falls back to a
1112    /// SHA-256 content hash (64 hex chars / 32 bytes).
1113    fn compute_signature(&self, content: &str) -> String {
1114        match &self.key_pair {
1115            Some(kp) => kp.sign(content),
1116            None => Self::compute_sha256_signature(content),
1117        }
1118    }
1119
1120    /// Set the Ed25519 key pair used for signing outgoing messages.
1121    pub fn set_signing_key(&mut self, key_pair: CommKeyPair) {
1122        self.key_pair = Some(key_pair);
1123    }
1124
1125    /// Get the hex-encoded Ed25519 public key, if a key pair is set.
1126    pub fn get_public_key(&self) -> Option<String> {
1127        self.key_pair.as_ref().map(|kp| kp.public_key_hex())
1128    }
1129
1130    /// Verify that a message's signature is valid.
1131    ///
1132    /// Ed25519 signatures are 64 bytes (128 hex chars). If the stored
1133    /// signature is that length and a key pair is set, Ed25519 verification
1134    /// is attempted first. Falls back to SHA-256 hash comparison for
1135    /// legacy signatures (64 hex chars / 32 bytes).
1136    ///
1137    /// Returns true if valid (or no signature stored), false if mismatch.
1138    pub fn verify_message_signature(&mut self, message_id: u64) -> bool {
1139        let (content, stored_sig, sender) = match self.messages.get(&message_id) {
1140            Some(msg) => (
1141                msg.content.clone(),
1142                msg.signature.clone(),
1143                msg.sender.clone(),
1144            ),
1145            None => return true,
1146        };
1147        match stored_sig {
1148            Some(ref sig) => {
1149                // Ed25519 signatures are 128 hex chars (64 bytes).
1150                // SHA-256 hashes are 64 hex chars (32 bytes).
1151                let valid = if sig.len() == 128 {
1152                    // Try Ed25519 verification if we have a public key
1153                    if let Some(ref kp) = self.key_pair {
1154                        crypto::verify_signature(&kp.public_key_hex(), &content, sig)
1155                    } else {
1156                        // No key pair set — cannot verify Ed25519 sig
1157                        false
1158                    }
1159                } else {
1160                    // Legacy SHA-256 hash comparison
1161                    let expected = Self::compute_sha256_signature(&content);
1162                    *sig == expected
1163                };
1164
1165                if !valid {
1166                    self.log_audit(
1167                        AuditEventType::SignatureWarning,
1168                        &sender,
1169                        &format!(
1170                            "Signature mismatch for message {}: stored={}",
1171                            message_id, sig
1172                        ),
1173                        Some(message_id.to_string()),
1174                    );
1175                }
1176                valid
1177            }
1178            None => true,
1179        }
1180    }
1181
1182    /// Check consent for sending a message to participants on the channel.
1183    /// For affect-enriched or other rich content types, requires explicit
1184    /// SendMessages consent from each recipient on the channel.
1185    fn check_send_consent(
1186        &self,
1187        channel_id: u64,
1188        sender: &str,
1189        content: &str,
1190    ) -> CommResult<()> {
1191        let channel = match self.channels.get(&channel_id) {
1192            Some(ch) => ch,
1193            None => return Ok(()),
1194        };
1195
1196        // Detect rich content that needs explicit consent
1197        let is_rich_content = content.starts_with("[affect:");
1198
1199        if !is_rich_content {
1200            return Ok(());
1201        }
1202
1203        // For rich content, check that each participant (other than sender) has
1204        // granted SendMessages consent to the sender.
1205        for participant in &channel.participants {
1206            if participant == sender {
1207                continue;
1208            }
1209            let has_consent = self.consent_gates.iter().any(|e| {
1210                e.grantor == *participant
1211                    && e.grantee == sender
1212                    && e.scope == ConsentScope::SendMessages
1213                    && e.status == ConsentStatus::Granted
1214            });
1215            if !has_consent {
1216                return Err(CommError::ConsentDenied {
1217                    reason: format!(
1218                        "Participant '{}' has not granted SendMessages consent to '{}'",
1219                        participant, sender
1220                    ),
1221                });
1222            }
1223        }
1224
1225        Ok(())
1226    }
1227
1228    /// Check and enforce rate limits for a sender. Returns Ok(()) if allowed,
1229    /// or RateLimitExceeded if the sender has exceeded the configured rate.
1230    fn check_rate_limit(&mut self, sender: &str) -> CommResult<()> {
1231        let now_epoch = Utc::now().timestamp() as u64;
1232        let limit_per_minute = self.rate_limit_config.messages_per_minute;
1233
1234        let tracker = self
1235            .rate_trackers
1236            .entry(sender.to_string())
1237            .or_default();
1238
1239        // Reset minute window if more than 60 seconds have elapsed
1240        if now_epoch - tracker.last_minute_reset >= 60 {
1241            tracker.message_count_minute = 0;
1242            tracker.last_minute_reset = now_epoch;
1243        }
1244
1245        // Reset hour window if more than 3600 seconds have elapsed
1246        if now_epoch - tracker.last_hour_reset >= 3600 {
1247            tracker.message_count_hour = 0;
1248            tracker.last_hour_reset = now_epoch;
1249        }
1250
1251        // Check minute limit
1252        if tracker.message_count_minute >= limit_per_minute {
1253            return Err(CommError::RateLimitExceeded {
1254                limit: format!(
1255                    "{} messages per minute (sender: {})",
1256                    limit_per_minute, sender
1257                ),
1258            });
1259        }
1260
1261        // Increment counters
1262        tracker.message_count_minute += 1;
1263        tracker.message_count_hour += 1;
1264
1265        Ok(())
1266    }
1267
1268    /// Check that a channel allows sending. Returns Ok(()) if allowed,
1269    /// or an appropriate error if the channel is in a blocking state.
1270    fn check_channel_allows_send(&self, channel_id: u64) -> CommResult<()> {
1271        let channel = self
1272            .channels
1273            .get(&channel_id)
1274            .ok_or(CommError::ChannelNotFound(channel_id))?;
1275
1276        match channel.state {
1277            ChannelState::Active => Ok(()),
1278            ChannelState::SilentCommunion | ChannelState::HiveMode => Ok(()),
1279            ChannelState::Paused => Err(CommError::ChannelStateViolation(
1280                channel_id,
1281                "paused".to_string(),
1282            )),
1283            ChannelState::Draining => Err(CommError::ChannelStateViolation(
1284                channel_id,
1285                "draining".to_string(),
1286            )),
1287            ChannelState::Closed => Err(CommError::ChannelStateViolation(
1288                channel_id,
1289                "closed".to_string(),
1290            )),
1291            ChannelState::Archived => Err(CommError::ChannelStateViolation(
1292                channel_id,
1293                "archived".to_string(),
1294            )),
1295            ChannelState::PendingConsent => Err(CommError::ChannelStateViolation(
1296                channel_id,
1297                "pending_consent".to_string(),
1298            )),
1299        }
1300    }
1301
1302    /// Check that a channel allows receiving. Returns Ok(()) if allowed.
1303    fn check_channel_allows_receive(&self, channel_id: u64) -> CommResult<()> {
1304        let channel = self
1305            .channels
1306            .get(&channel_id)
1307            .ok_or(CommError::ChannelNotFound(channel_id))?;
1308
1309        match channel.state {
1310            ChannelState::Active | ChannelState::Draining => Ok(()),
1311            ChannelState::SilentCommunion | ChannelState::HiveMode => Ok(()),
1312            ChannelState::Archived => Ok(()),
1313            ChannelState::Paused => Err(CommError::ChannelStateViolation(
1314                channel_id,
1315                "paused".to_string(),
1316            )),
1317            ChannelState::Closed => Err(CommError::ChannelStateViolation(
1318                channel_id,
1319                "closed".to_string(),
1320            )),
1321            ChannelState::PendingConsent => Err(CommError::ChannelStateViolation(
1322                channel_id,
1323                "pending_consent".to_string(),
1324            )),
1325        }
1326    }
1327
1328    /// Check that an agent's trust level meets a channel's minimum requirement.
1329    ///
1330    /// Returns `Ok(())` if the channel has no `min_trust_level` or if the
1331    /// agent's trust level meets or exceeds it.  Returns `TrustError`
1332    /// otherwise.
1333    fn check_trust_for_channel(&self, agent: &str, channel_id: u64) -> CommResult<()> {
1334        let channel = self
1335            .channels
1336            .get(&channel_id)
1337            .ok_or(CommError::ChannelNotFound(channel_id))?;
1338        if let Some(min_trust) = channel.config.min_trust_level {
1339            let agent_trust = self.get_trust_level(agent);
1340            if agent_trust < min_trust {
1341                return Err(CommError::TrustError(format!(
1342                    "Trust level insufficient: {:?} < {:?}",
1343                    agent_trust, min_trust
1344                )));
1345            }
1346        }
1347        Ok(())
1348    }
1349
1350    /// General consent check for an agent performing an action in a given scope.
1351    ///
1352    /// Returns `true` (allow) when:
1353    /// - No consent gate exists for the requested scope (open-by-default), OR
1354    /// - An explicit `Granted` entry exists for the agent + scope.
1355    ///
1356    /// Returns `false` (deny) when:
1357    /// - A consent gate exists for the scope but the agent's entry is not
1358    ///   `Granted` (i.e. it is `Denied`, `Revoked`, `Pending`, or `Expired`).
1359    fn check_consent_for_action(&self, agent: &str, _resource: &str, scope: ConsentScope) -> bool {
1360        // Collect all gates matching this scope
1361        let scope_gates: Vec<&ConsentGateEntry> = self
1362            .consent_gates
1363            .iter()
1364            .filter(|e| e.scope == scope)
1365            .collect();
1366
1367        // Open-by-default: if there are no gates for this scope at all, allow.
1368        if scope_gates.is_empty() {
1369            return true;
1370        }
1371
1372        // Check if the agent has an explicit Granted entry (as grantee)
1373        scope_gates.iter().any(|e| {
1374            e.grantee == agent && e.status == ConsentStatus::Granted
1375        })
1376    }
1377
1378    // -----------------------------------------------------------------------
1379    // Channel state management
1380    // -----------------------------------------------------------------------
1381
1382    /// Pause a channel. Blocks new sends and receives.
1383    pub fn pause_channel(&mut self, channel_id: u64) -> CommResult<()> {
1384        let channel = self
1385            .channels
1386            .get_mut(&channel_id)
1387            .ok_or(CommError::ChannelNotFound(channel_id))?;
1388        channel.state = ChannelState::Paused;
1389        Ok(())
1390    }
1391
1392    /// Resume a paused channel back to Active state.
1393    pub fn resume_channel(&mut self, channel_id: u64) -> CommResult<()> {
1394        let channel = self
1395            .channels
1396            .get_mut(&channel_id)
1397            .ok_or(CommError::ChannelNotFound(channel_id))?;
1398        channel.state = ChannelState::Active;
1399        Ok(())
1400    }
1401
1402    /// Set a channel to Draining state. Allows receive but blocks send.
1403    pub fn drain_channel(&mut self, channel_id: u64) -> CommResult<()> {
1404        let channel = self
1405            .channels
1406            .get_mut(&channel_id)
1407            .ok_or(CommError::ChannelNotFound(channel_id))?;
1408        channel.state = ChannelState::Draining;
1409        Ok(())
1410    }
1411
1412    /// Close a channel. Blocks all operations.
1413    pub fn close_channel(&mut self, channel_id: u64) -> CommResult<()> {
1414        let channel = self
1415            .channels
1416            .get_mut(&channel_id)
1417            .ok_or(CommError::ChannelNotFound(channel_id))?;
1418        let name = channel.name.clone();
1419        channel.state = ChannelState::Closed;
1420
1421        // --- Audit logging ---
1422        self.log_audit(
1423            AuditEventType::ChannelClosed,
1424            "system",
1425            &format!("Closed channel '{}' (id={})", name, channel_id),
1426            Some(channel_id.to_string()),
1427        );
1428
1429        Ok(())
1430    }
1431
1432    // -----------------------------------------------------------------------
1433    // Message engine
1434    // -----------------------------------------------------------------------
1435
1436    /// Send a message to a channel.
1437    ///
1438    /// Enforces rate limiting, consent gates, and channel state before
1439    /// delivering. If the channel is Paused, Draining, or Closed, the
1440    /// message is automatically dead-lettered and an error is returned.
1441    pub fn send_message(
1442        &mut self,
1443        channel_id: u64,
1444        sender: &str,
1445        content: &str,
1446        msg_type: MessageType,
1447    ) -> CommResult<Message> {
1448        Self::validate_sender(sender)?;
1449        Self::validate_content(content)?;
1450
1451        // --- Rate limiting (before any other processing) ---
1452        self.check_rate_limit(sender)?;
1453
1454        // --- Consent enforcement ---
1455        self.check_send_consent(channel_id, sender, content)?;
1456
1457        // Check channel existence
1458        if !self.channels.contains_key(&channel_id) {
1459            // Dead-letter the message for channel not found
1460            let id = self.next_message_id;
1461            self.next_message_id += 1;
1462            let msg = Message {
1463                id,
1464                channel_id,
1465                sender: sender.to_string(),
1466                recipient: None,
1467                content: content.to_string(),
1468                message_type: msg_type,
1469                timestamp: Utc::now(),
1470                metadata: HashMap::new(),
1471                signature: Some(self.compute_signature(content)),
1472                acknowledged_by: Vec::new(),
1473                status: MessageStatus::DeadLettered,
1474                priority: MessagePriority::default(),
1475                reply_to: None,
1476                correlation_id: None,
1477                thread_id: None,
1478                comm_timestamp: CommTimestamp::default(),
1479                rich_content_json: None,
1480                comm_id: None,
1481                receipt_id: None,
1482            };
1483            self.dead_letters.push(DeadLetter {
1484                original_message: msg,
1485                reason: DeadLetterReason::ChannelNotFound,
1486                dead_lettered_at: Utc::now(),
1487                retry_count: 0,
1488            });
1489            return Err(CommError::ChannelNotFound(channel_id));
1490        }
1491
1492        // --- Trust enforcement ---
1493        self.check_trust_for_channel(sender, channel_id)?;
1494
1495        // Check channel state — dead-letter on violation
1496        if let Err(e) = self.check_channel_allows_send(channel_id) {
1497            let id = self.next_message_id;
1498            self.next_message_id += 1;
1499            let channel_state = self.channels.get(&channel_id).unwrap().state;
1500            let reason = match channel_state {
1501                ChannelState::Closed => DeadLetterReason::ChannelClosed,
1502                _ => DeadLetterReason::ValidationFailed(format!(
1503                    "Channel is {}",
1504                    channel_state
1505                )),
1506            };
1507            let msg = Message {
1508                id,
1509                channel_id,
1510                sender: sender.to_string(),
1511                recipient: None,
1512                content: content.to_string(),
1513                message_type: msg_type,
1514                timestamp: Utc::now(),
1515                metadata: HashMap::new(),
1516                signature: Some(self.compute_signature(content)),
1517                acknowledged_by: Vec::new(),
1518                status: MessageStatus::DeadLettered,
1519                priority: MessagePriority::default(),
1520                reply_to: None,
1521                correlation_id: None,
1522                thread_id: None,
1523                comm_timestamp: CommTimestamp::default(),
1524                rich_content_json: None,
1525                comm_id: None,
1526                receipt_id: None,
1527            };
1528            self.dead_letters.push(DeadLetter {
1529                original_message: msg,
1530                reason,
1531                dead_lettered_at: Utc::now(),
1532                retry_count: 0,
1533            });
1534            return Err(e);
1535        }
1536
1537        let id = self.next_message_id;
1538        self.next_message_id += 1;
1539
1540        // Increment Lamport counter for causal ordering
1541        self.lamport_counter += 1;
1542        let mut ts = CommTimestamp::now(sender);
1543        ts.lamport = self.lamport_counter;
1544        ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1545
1546        let message = Message {
1547            id,
1548            channel_id,
1549            sender: sender.to_string(),
1550            recipient: None,
1551            content: content.to_string(),
1552            message_type: msg_type,
1553            timestamp: Utc::now(),
1554            metadata: HashMap::new(),
1555            signature: Some(self.compute_signature(content)),
1556            acknowledged_by: Vec::new(),
1557            status: MessageStatus::Sent,
1558            priority: MessagePriority::default(),
1559            reply_to: None,
1560            correlation_id: None,
1561            thread_id: None,
1562            comm_timestamp: ts,
1563            rich_content_json: None,
1564            comm_id: None,
1565            receipt_id: None,
1566        };
1567
1568        self.messages.insert(id, message.clone());
1569
1570        // --- Audit logging ---
1571        self.log_audit(
1572            AuditEventType::MessageSent,
1573            sender,
1574            &format!("Sent {} message to channel {}", msg_type, channel_id),
1575            Some(id.to_string()),
1576        );
1577
1578        // Bridge point: memory_bridge.log_conversation() for temporal chaining
1579
1580        Ok(message)
1581    }
1582
1583    /// Send a message with a specific priority.
1584    pub fn send_message_with_priority(
1585        &mut self,
1586        channel_id: u64,
1587        sender: &str,
1588        content: &str,
1589        msg_type: MessageType,
1590        priority: MessagePriority,
1591    ) -> CommResult<Message> {
1592        let mut msg = self.send_message(channel_id, sender, content, msg_type)?;
1593        // Update priority on the stored message
1594        if let Some(stored) = self.messages.get_mut(&msg.id) {
1595            stored.priority = priority;
1596            msg.priority = priority;
1597        }
1598        Ok(msg)
1599    }
1600
1601    /// Receive messages from a channel, optionally filtered by recipient and time.
1602    ///
1603    /// Verifies message signatures on retrieval and logs a warning audit
1604    /// event if any signature does not match. Mismatched messages are still
1605    /// returned (reads are never blocked).
1606    pub fn receive_messages(
1607        &mut self,
1608        channel_id: u64,
1609        recipient: Option<&str>,
1610        since: Option<DateTime<Utc>>,
1611    ) -> CommResult<Vec<Message>> {
1612        if !self.channels.contains_key(&channel_id) {
1613            return Err(CommError::ChannelNotFound(channel_id));
1614        }
1615
1616        // Draining channels allow receive; only Paused and Closed block it
1617        self.check_channel_allows_receive(channel_id)?;
1618
1619        let mut msgs: Vec<Message> = self
1620            .messages
1621            .values()
1622            .filter(|m| {
1623                if m.channel_id != channel_id {
1624                    return false;
1625                }
1626                if let Some(ref recip) = recipient {
1627                    if let Some(ref msg_recip) = m.recipient {
1628                        if msg_recip != recip {
1629                            return false;
1630                        }
1631                    }
1632                }
1633                if let Some(ref s) = since {
1634                    if m.timestamp < *s {
1635                        return false;
1636                    }
1637                }
1638                true
1639            })
1640            .cloned()
1641            .collect();
1642
1643        msgs.sort_by_key(|m| m.timestamp);
1644
1645        // --- Signature verification on retrieval ---
1646        let msg_ids: Vec<u64> = msgs.iter().map(|m| m.id).collect();
1647        for msg_id in msg_ids {
1648            self.verify_message_signature(msg_id);
1649        }
1650
1651        // --- Vector clock merge on receive ---
1652        // Merge each message's vector clock into the store's lamport counter
1653        // so that subsequent sends reflect causal awareness of received messages.
1654        for m in &msgs {
1655            if m.comm_timestamp.lamport > self.lamport_counter {
1656                self.lamport_counter = m.comm_timestamp.lamport;
1657            }
1658        }
1659
1660        Ok(msgs)
1661    }
1662
1663    /// Acknowledge receipt of a message.
1664    pub fn acknowledge_message(&mut self, message_id: u64, recipient: &str) -> CommResult<()> {
1665        Self::validate_sender(recipient)?;
1666
1667        let message = self
1668            .messages
1669            .get_mut(&message_id)
1670            .ok_or(CommError::MessageNotFound(message_id))?;
1671
1672        if !message.acknowledged_by.contains(&recipient.to_string()) {
1673            message.acknowledged_by.push(recipient.to_string());
1674        }
1675        message.status = MessageStatus::Acknowledged;
1676        Ok(())
1677    }
1678
1679    /// Broadcast a message to all participants in a broadcast channel.
1680    pub fn broadcast(
1681        &mut self,
1682        channel_id: u64,
1683        sender: &str,
1684        content: &str,
1685    ) -> CommResult<Vec<Message>> {
1686        Self::validate_sender(sender)?;
1687        Self::validate_content(content)?;
1688
1689        // --- Trust enforcement ---
1690        self.check_trust_for_channel(sender, channel_id)?;
1691
1692        let channel = self
1693            .channels
1694            .get(&channel_id)
1695            .ok_or(CommError::ChannelNotFound(channel_id))?
1696            .clone();
1697
1698        let mut delivered = Vec::new();
1699
1700        for participant in &channel.participants {
1701            if participant == sender {
1702                continue;
1703            }
1704
1705            let id = self.next_message_id;
1706            self.next_message_id += 1;
1707
1708            // Increment Lamport counter for each broadcast copy
1709            self.lamport_counter += 1;
1710            let mut ts = CommTimestamp::now(sender);
1711            ts.lamport = self.lamport_counter;
1712            ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1713
1714            let message = Message {
1715                id,
1716                channel_id,
1717                sender: sender.to_string(),
1718                recipient: Some(participant.clone()),
1719                content: content.to_string(),
1720                message_type: MessageType::Broadcast,
1721                timestamp: Utc::now(),
1722                metadata: HashMap::new(),
1723                signature: Some(self.compute_signature(content)),
1724                acknowledged_by: Vec::new(),
1725                status: MessageStatus::Sent,
1726                priority: MessagePriority::default(),
1727                reply_to: None,
1728                correlation_id: None,
1729                thread_id: None,
1730                comm_timestamp: ts,
1731                rich_content_json: None,
1732                comm_id: None,
1733                receipt_id: None,
1734            };
1735
1736            self.messages.insert(id, message.clone());
1737            delivered.push(message);
1738        }
1739
1740        Ok(delivered)
1741    }
1742
1743    // -----------------------------------------------------------------------
1744    // Message threading
1745    // -----------------------------------------------------------------------
1746
1747    /// Send a reply linked to a parent message.
1748    pub fn send_reply(
1749        &mut self,
1750        channel_id: u64,
1751        message_id: u64,
1752        sender: &str,
1753        content: &str,
1754        msg_type: MessageType,
1755    ) -> CommResult<Message> {
1756        Self::validate_sender(sender)?;
1757        Self::validate_content(content)?;
1758
1759        // Verify parent message exists
1760        let parent = self
1761            .messages
1762            .get(&message_id)
1763            .ok_or(CommError::MessageNotFound(message_id))?
1764            .clone();
1765
1766        // Verify channel exists and allows sending
1767        self.check_channel_allows_send(channel_id)?;
1768
1769        // Inherit thread_id from parent, or use parent's id as thread_id
1770        let thread_id = parent
1771            .thread_id
1772            .clone()
1773            .unwrap_or_else(|| format!("thread-{}", parent.id));
1774
1775        let id = self.next_message_id;
1776        self.next_message_id += 1;
1777
1778        // Increment Lamport counter for causal ordering
1779        self.lamport_counter += 1;
1780        let mut ts = CommTimestamp::now(sender);
1781        ts.lamport = self.lamport_counter;
1782        ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
1783
1784        let message = Message {
1785            id,
1786            channel_id,
1787            sender: sender.to_string(),
1788            recipient: None,
1789            content: content.to_string(),
1790            message_type: msg_type,
1791            timestamp: Utc::now(),
1792            metadata: HashMap::new(),
1793            signature: Some(self.compute_signature(content)),
1794            acknowledged_by: Vec::new(),
1795            status: MessageStatus::Sent,
1796            priority: MessagePriority::default(),
1797            reply_to: Some(message_id),
1798            correlation_id: None,
1799            thread_id: Some(thread_id),
1800            comm_timestamp: ts,
1801            rich_content_json: None,
1802            comm_id: None,
1803            receipt_id: None,
1804        };
1805
1806        self.messages.insert(id, message.clone());
1807
1808        // Also set the parent's thread_id if it wasn't set yet
1809        if let Some(parent_msg) = self.messages.get_mut(&message_id) {
1810            if parent_msg.thread_id.is_none() {
1811                parent_msg.thread_id = Some(format!("thread-{}", parent_msg.id));
1812            }
1813        }
1814
1815        Ok(message)
1816    }
1817
1818    /// Get all messages in a thread, ordered by timestamp.
1819    pub fn get_thread(&self, thread_id: &str) -> Vec<Message> {
1820        let mut msgs: Vec<Message> = self
1821            .messages
1822            .values()
1823            .filter(|m| m.thread_id.as_deref() == Some(thread_id))
1824            .cloned()
1825            .collect();
1826        msgs.sort_by_key(|m| m.timestamp);
1827        msgs
1828    }
1829
1830    /// Get all direct replies to a specific message.
1831    pub fn get_replies(&self, message_id: u64) -> Vec<Message> {
1832        let mut replies: Vec<Message> = self
1833            .messages
1834            .values()
1835            .filter(|m| m.reply_to == Some(message_id))
1836            .cloned()
1837            .collect();
1838        replies.sort_by_key(|m| m.timestamp);
1839        replies
1840    }
1841
1842    // -----------------------------------------------------------------------
1843    // Channel management
1844    // -----------------------------------------------------------------------
1845
1846    /// Create a new communication channel.
1847    pub fn create_channel(
1848        &mut self,
1849        name: &str,
1850        channel_type: ChannelType,
1851        config: Option<ChannelConfig>,
1852    ) -> CommResult<Channel> {
1853        Self::validate_channel_name(name)?;
1854
1855        let id = self.next_channel_id;
1856        self.next_channel_id += 1;
1857
1858        let channel = Channel {
1859            id,
1860            name: name.to_string(),
1861            channel_type,
1862            created_at: Utc::now(),
1863            participants: Vec::new(),
1864            config: config.unwrap_or_default(),
1865            state: ChannelState::Active,
1866            comm_id: None,
1867            contract_ref: None,
1868        };
1869
1870        self.channels.insert(id, channel.clone());
1871
1872        // --- Audit logging ---
1873        self.log_audit(
1874            AuditEventType::ChannelCreated,
1875            "system",
1876            &format!("Created channel '{}' (type={}, id={})", name, channel_type, id),
1877            Some(id.to_string()),
1878        );
1879
1880        Ok(channel)
1881    }
1882
1883    /// Join a channel as a participant.
1884    pub fn join_channel(&mut self, channel_id: u64, participant: &str) -> CommResult<()> {
1885        Self::validate_sender(participant)?;
1886
1887        // Bridge point: identity_bridge.resolve_identity() for participant verification
1888
1889        // --- Trust enforcement ---
1890        self.check_trust_for_channel(participant, channel_id)?;
1891
1892        // --- Consent enforcement ---
1893        if !self.check_consent_for_action(participant, &self.channels.get(&channel_id)
1894            .map(|c| c.name.clone()).unwrap_or_default(), ConsentScope::JoinChannels)
1895        {
1896            return Err(CommError::ConsentDenied {
1897                reason: "Consent not granted for joining channels".to_string(),
1898            });
1899        }
1900
1901        let channel = self
1902            .channels
1903            .get_mut(&channel_id)
1904            .ok_or(CommError::ChannelNotFound(channel_id))?;
1905
1906        if channel.participants.contains(&participant.to_string()) {
1907            return Err(CommError::AlreadyInChannel(
1908                participant.to_string(),
1909                channel_id,
1910            ));
1911        }
1912
1913        if channel.config.max_participants > 0
1914            && channel.participants.len() >= channel.config.max_participants as usize
1915        {
1916            return Err(CommError::ChannelFull(channel_id));
1917        }
1918
1919        channel.participants.push(participant.to_string());
1920        Ok(())
1921    }
1922
1923    /// Leave a channel.
1924    pub fn leave_channel(&mut self, channel_id: u64, participant: &str) -> CommResult<()> {
1925        let channel = self
1926            .channels
1927            .get_mut(&channel_id)
1928            .ok_or(CommError::ChannelNotFound(channel_id))?;
1929
1930        let pos = channel
1931            .participants
1932            .iter()
1933            .position(|p| p == participant)
1934            .ok_or_else(|| CommError::NotInChannel(participant.to_string(), channel_id))?;
1935
1936        channel.participants.remove(pos);
1937        Ok(())
1938    }
1939
1940    /// List all channels.
1941    pub fn list_channels(&self) -> Vec<Channel> {
1942        let mut channels: Vec<Channel> = self.channels.values().cloned().collect();
1943        channels.sort_by_key(|c| c.id);
1944        channels
1945    }
1946
1947    /// Get a specific channel by id.
1948    pub fn get_channel(&self, channel_id: u64) -> Option<Channel> {
1949        self.channels.get(&channel_id).cloned()
1950    }
1951
1952    /// Update channel configuration.
1953    pub fn set_channel_config(
1954        &mut self,
1955        channel_id: u64,
1956        config: ChannelConfig,
1957    ) -> CommResult<()> {
1958        let channel = self
1959            .channels
1960            .get_mut(&channel_id)
1961            .ok_or(CommError::ChannelNotFound(channel_id))?;
1962        channel.config = config;
1963        Ok(())
1964    }
1965
1966    // -----------------------------------------------------------------------
1967    // Pub/Sub
1968    // -----------------------------------------------------------------------
1969
1970    /// Subscribe to a topic.
1971    pub fn subscribe(&mut self, topic: &str, subscriber: &str) -> CommResult<Subscription> {
1972        Self::validate_sender(subscriber)?;
1973        Self::validate_channel_name(topic)?;
1974
1975        let id = self.next_subscription_id;
1976        self.next_subscription_id += 1;
1977
1978        let subscription = Subscription {
1979            id,
1980            topic: topic.to_string(),
1981            subscriber: subscriber.to_string(),
1982            created_at: Utc::now(),
1983        };
1984
1985        self.subscriptions.insert(id, subscription.clone());
1986        Ok(subscription)
1987    }
1988
1989    /// Remove a subscription.
1990    pub fn unsubscribe(&mut self, subscription_id: u64) -> CommResult<()> {
1991        if self.subscriptions.remove(&subscription_id).is_none() {
1992            return Err(CommError::SubscriptionNotFound(subscription_id));
1993        }
1994        Ok(())
1995    }
1996
1997    /// Publish a message to all subscribers of a topic.
1998    pub fn publish(
1999        &mut self,
2000        topic: &str,
2001        sender: &str,
2002        content: &str,
2003    ) -> CommResult<Vec<Message>> {
2004        Self::validate_sender(sender)?;
2005        Self::validate_content(content)?;
2006
2007        // Find or create the topic channel
2008        let channel_id = self
2009            .channels
2010            .values()
2011            .find(|c| c.name == topic && c.channel_type == ChannelType::PubSub)
2012            .map(|c| c.id);
2013
2014        let channel_id = match channel_id {
2015            Some(id) => id,
2016            None => {
2017                let ch = self.create_channel(topic, ChannelType::PubSub, None)?;
2018                ch.id
2019            }
2020        };
2021
2022        // --- Trust enforcement ---
2023        self.check_trust_for_channel(sender, channel_id)?;
2024
2025        // Get all subscribers for this topic
2026        let subscribers: Vec<String> = self
2027            .subscriptions
2028            .values()
2029            .filter(|s| s.topic == topic && s.subscriber != sender)
2030            .map(|s| s.subscriber.clone())
2031            .collect();
2032
2033        let mut delivered = Vec::new();
2034
2035        for subscriber in subscribers {
2036            let id = self.next_message_id;
2037            self.next_message_id += 1;
2038
2039            // Increment Lamport counter for each pub/sub delivery
2040            self.lamport_counter += 1;
2041            let mut ts = CommTimestamp::now(sender);
2042            ts.lamport = self.lamport_counter;
2043            ts.vector_clock.insert(sender.to_string(), self.lamport_counter);
2044
2045            let message = Message {
2046                id,
2047                channel_id,
2048                sender: sender.to_string(),
2049                recipient: Some(subscriber),
2050                content: content.to_string(),
2051                message_type: MessageType::Notification,
2052                timestamp: Utc::now(),
2053                metadata: HashMap::new(),
2054                signature: Some(self.compute_signature(content)),
2055                acknowledged_by: Vec::new(),
2056                status: MessageStatus::Sent,
2057                priority: MessagePriority::default(),
2058                reply_to: None,
2059                correlation_id: None,
2060                thread_id: None,
2061                comm_timestamp: ts,
2062                rich_content_json: None,
2063                comm_id: None,
2064                receipt_id: None,
2065            };
2066
2067            self.messages.insert(id, message.clone());
2068            delivered.push(message);
2069        }
2070
2071        Ok(delivered)
2072    }
2073
2074    // -----------------------------------------------------------------------
2075    // Dead letter queue
2076    // -----------------------------------------------------------------------
2077
2078    /// Return the number of dead letters in the queue.
2079    pub fn dead_letter_count(&self) -> usize {
2080        self.dead_letters.len()
2081    }
2082
2083    /// List all dead letters, sorted by dead-lettered time (oldest first).
2084    pub fn list_dead_letters(&self) -> Vec<DeadLetter> {
2085        let mut dl = self.dead_letters.clone();
2086        dl.sort_by_key(|d| d.dead_lettered_at);
2087        dl
2088    }
2089
2090    /// Attempt to replay (re-send) a dead letter by index.
2091    ///
2092    /// If the channel is now available and active, the message is re-sent
2093    /// and removed from the dead letter queue. Otherwise, the retry count
2094    /// is incremented and the dead letter remains.
2095    pub fn replay_dead_letter(&mut self, index: usize) -> CommResult<Message> {
2096        if index >= self.dead_letters.len() {
2097            return Err(CommError::DeadLetterNotFound(index));
2098        }
2099
2100        let dl = self.dead_letters[index].clone();
2101        let orig = &dl.original_message;
2102
2103        // Try to re-send
2104        match self.send_message(
2105            orig.channel_id,
2106            &orig.sender,
2107            &orig.content,
2108            orig.message_type,
2109        ) {
2110            Ok(msg) => {
2111                // Remove from dead letter queue on success
2112                self.dead_letters.remove(index);
2113                Ok(msg)
2114            }
2115            Err(e) => {
2116                // Increment retry count on the existing dead letter (the send_message
2117                // already created a new dead letter entry, so remove that duplicate
2118                // and just update the original)
2119                let new_len = self.dead_letters.len();
2120                // The failed send_message may have added a new dead letter at the end
2121                if new_len > dl.retry_count as usize + self.dead_letters.len() {
2122                    // Remove the duplicate that send_message just added
2123                    self.dead_letters.pop();
2124                }
2125                // The original dead letter is still at `index` (or shifted if something
2126                // was removed before it). Increment its retry count.
2127                if index < self.dead_letters.len() {
2128                    self.dead_letters[index].retry_count += 1;
2129                }
2130                Err(e)
2131            }
2132        }
2133    }
2134
2135    /// Clear all dead letters from the queue.
2136    pub fn clear_dead_letters(&mut self) {
2137        self.dead_letters.clear();
2138    }
2139
2140    // -----------------------------------------------------------------------
2141    // TTL enforcement
2142    // -----------------------------------------------------------------------
2143
2144    /// Expire messages that have exceeded their channel's TTL.
2145    ///
2146    /// Scans all messages. If the channel has `ttl_seconds > 0` and the
2147    /// message is older than the TTL, the message is moved to the dead
2148    /// letter queue with reason `Expired`.
2149    ///
2150    /// Returns the count of expired messages.
2151    pub fn expire_messages(&mut self) -> usize {
2152        let now = Utc::now();
2153        let mut expired_ids: Vec<u64> = Vec::new();
2154
2155        for msg in self.messages.values() {
2156            if let Some(channel) = self.channels.get(&msg.channel_id) {
2157                if channel.config.ttl_seconds > 0 {
2158                    let age = now
2159                        .signed_duration_since(msg.timestamp)
2160                        .num_seconds();
2161                    if age > channel.config.ttl_seconds as i64 {
2162                        expired_ids.push(msg.id);
2163                    }
2164                }
2165            }
2166        }
2167
2168        let count = expired_ids.len();
2169
2170        for id in expired_ids {
2171            if let Some(mut msg) = self.messages.remove(&id) {
2172                msg.status = MessageStatus::Expired;
2173                self.dead_letters.push(DeadLetter {
2174                    original_message: msg,
2175                    reason: DeadLetterReason::Expired,
2176                    dead_lettered_at: now,
2177                    retry_count: 0,
2178                });
2179            }
2180        }
2181
2182        count
2183    }
2184
2185    // -----------------------------------------------------------------------
2186    // Compact
2187    // -----------------------------------------------------------------------
2188
2189    /// Compact the store by removing messages from closed channels and
2190    /// enforcing retention policies.
2191    ///
2192    /// Returns the count of removed messages.
2193    pub fn compact(&mut self) -> usize {
2194        let mut removed = 0usize;
2195
2196        // 1. Remove messages from closed channels
2197        let closed_channel_ids: Vec<u64> = self
2198            .channels
2199            .values()
2200            .filter(|c| c.state == ChannelState::Closed)
2201            .map(|c| c.id)
2202            .collect();
2203
2204        let ids_to_remove: Vec<u64> = self
2205            .messages
2206            .values()
2207            .filter(|m| closed_channel_ids.contains(&m.channel_id))
2208            .map(|m| m.id)
2209            .collect();
2210
2211        for id in ids_to_remove {
2212            self.messages.remove(&id);
2213            removed += 1;
2214        }
2215
2216        // 2. Enforce RetentionPolicy::MessageCount per channel
2217        for channel in self.channels.values() {
2218            if let RetentionPolicy::MessageCount(max_count) = channel.config.retention_policy {
2219                let mut channel_msgs: Vec<(u64, DateTime<Utc>)> = self
2220                    .messages
2221                    .values()
2222                    .filter(|m| m.channel_id == channel.id)
2223                    .map(|m| (m.id, m.timestamp))
2224                    .collect();
2225
2226                if channel_msgs.len() > max_count as usize {
2227                    // Sort by timestamp ascending (oldest first)
2228                    channel_msgs.sort_by_key(|&(_, ts)| ts);
2229                    let to_remove = channel_msgs.len() - max_count as usize;
2230                    for (id, _) in channel_msgs.into_iter().take(to_remove) {
2231                        self.messages.remove(&id);
2232                        removed += 1;
2233                    }
2234                }
2235            }
2236        }
2237
2238        removed
2239    }
2240
2241    // -----------------------------------------------------------------------
2242    // Query engine
2243    // -----------------------------------------------------------------------
2244
2245    /// Query message history with filters.
2246    pub fn query_history(&self, channel_id: u64, filter: &MessageFilter) -> Vec<Message> {
2247        let mut results: Vec<Message> = self
2248            .messages
2249            .values()
2250            .filter(|m| {
2251                if m.channel_id != channel_id {
2252                    return false;
2253                }
2254                if let Some(ref since) = filter.since {
2255                    if m.timestamp < *since {
2256                        return false;
2257                    }
2258                }
2259                if let Some(ref before) = filter.before {
2260                    if m.timestamp > *before {
2261                        return false;
2262                    }
2263                }
2264                if let Some(ref sender) = filter.sender {
2265                    if m.sender != *sender {
2266                        return false;
2267                    }
2268                }
2269                if let Some(ref msg_type) = filter.message_type {
2270                    if m.message_type != *msg_type {
2271                        return false;
2272                    }
2273                }
2274                if let Some(priority_val) = filter.priority {
2275                    let msg_priority = m.priority as u32;
2276                    if msg_priority != priority_val {
2277                        return false;
2278                    }
2279                }
2280                if let Some(filter_thread) = filter.thread_id {
2281                    match &m.thread_id {
2282                        Some(tid) => {
2283                            if let Ok(parsed) = tid.parse::<u64>() {
2284                                if parsed != filter_thread {
2285                                    return false;
2286                                }
2287                            } else {
2288                                return false;
2289                            }
2290                        }
2291                        None => return false,
2292                    }
2293                }
2294                if let Some(ref substr) = filter.content_contains {
2295                    if !m.content.to_lowercase().contains(&substr.to_lowercase()) {
2296                        return false;
2297                    }
2298                }
2299                true
2300            })
2301            .cloned()
2302            .collect();
2303
2304        results.sort_by_key(|m| m.timestamp);
2305
2306        if let Some(limit) = filter.limit {
2307            results.truncate(limit);
2308        }
2309
2310        results
2311    }
2312
2313    /// Full-text search across all messages.
2314    pub fn search_messages(&self, query_text: &str, max_results: usize) -> Vec<Message> {
2315        let query_lower = query_text.to_lowercase();
2316        let mut results: Vec<Message> = self
2317            .messages
2318            .values()
2319            .filter(|m| m.content.to_lowercase().contains(&query_lower))
2320            .cloned()
2321            .collect();
2322
2323        results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
2324        results.truncate(max_results);
2325        results
2326    }
2327
2328    /// Get a specific message by id.
2329    pub fn get_message(&self, message_id: u64) -> Option<Message> {
2330        self.messages.get(&message_id).cloned()
2331    }
2332
2333    // -----------------------------------------------------------------------
2334    // Persistence (.acomm file format)
2335    // -----------------------------------------------------------------------
2336
2337    /// Save the store to a `.acomm` file (bincode + zstd + binary header).
2338    ///
2339    /// Acquires an exclusive [`CommFileLock`] for the duration of the write so
2340    /// that concurrent readers/writers on the same path do not corrupt data.
2341    ///
2342    /// The on-disk format is: `[ACOM header (48 bytes)] [zstd(bincode(store))]`.
2343    /// The ACOM header includes a Blake3 hash of the compressed payload so
2344    /// that corruption can be detected on load. The FLAG_ZSTD flag (bit 0) is
2345    /// set to indicate Zstd compression; older files without this flag are
2346    /// treated as gzip-compressed on read.
2347    pub fn save(&self, path: &Path) -> CommResult<()> {
2348        // Recover stale locks (older than 60 s) before attempting to acquire.
2349        CommFileLock::recover_stale(path, 60)?;
2350
2351        let _lock = CommFileLock::acquire(path)?;
2352
2353        let store_bytes =
2354            bincode::serialize(self).map_err(|e| CommError::Serialization(e.to_string()))?;
2355
2356        // Zstd-compress the bincode payload (level 3 for fast compression).
2357        let compressed = zstd::bulk::compress(&store_bytes, 3)
2358            .map_err(|e| CommError::Serialization(format!("Zstd compression failed: {e}")))?;
2359
2360        // Wrap the compressed data with the binary format header (magic + Blake3)
2361        // and set FLAG_ZSTD so readers know to use Zstd decompression.
2362        let output = format::write_with_header_flags(&compressed, format::FLAG_ZSTD);
2363
2364        let mut file = std::fs::File::create(path)?;
2365        file.write_all(&output)?;
2366
2367        // Lock released via Drop of `_lock`.
2368        Ok(())
2369    }
2370
2371    /// Load a store from a `.acomm` file.
2372    ///
2373    /// Acquires a shared [`CommFileLock`] for the duration of the read so that
2374    /// concurrent writers are held off while the data is being read.
2375    ///
2376    /// Supports three on-disk variants:
2377    /// - **v3 with FLAG_ZSTD** (current): ACOM header + Zstd-compressed payload.
2378    /// - **v2/v3 without FLAG_ZSTD** (legacy): ACOM header + gzip-compressed payload.
2379    /// - **v1** (oldest): raw gzip with embedded ACOMM001 header.
2380    pub fn load(path: &Path) -> CommResult<Self> {
2381        // Recover stale locks (older than 60 s) before attempting to acquire.
2382        CommFileLock::recover_stale(path, 60)?;
2383
2384        let _lock = CommFileLock::acquire_shared(path)?;
2385
2386        let mut raw = Vec::new();
2387        {
2388            let mut file = std::fs::File::open(path)?;
2389            file.read_to_end(&mut raw)?;
2390        }
2391
2392        if format::is_new_format(&raw) {
2393            // ---- New binary format (v2/v3) ----
2394            let (header, compressed_data) = format::read_with_header_and_meta(&raw)
2395                .map_err(|e| CommError::InvalidFile(e))?;
2396
2397            let decompressed = if header.is_zstd() {
2398                // Zstd-compressed payload (current format).
2399                zstd::bulk::decompress(&compressed_data, 64 * 1024 * 1024)
2400                    .map_err(|e| CommError::InvalidFile(
2401                        format!("Zstd decompression failed: {e}"),
2402                    ))?
2403            } else {
2404                // Gzip-compressed payload (legacy v2/v3 files).
2405                let mut decoder = GzDecoder::new(&compressed_data[..]);
2406                let mut buf = Vec::new();
2407                decoder.read_to_end(&mut buf)?;
2408                buf
2409            };
2410
2411            let store: CommStore = bincode::deserialize(&decompressed)
2412                .map_err(|e| CommError::InvalidFile(format!("Bad store data: {e}")))?;
2413            Ok(store)
2414        } else {
2415            // ---- Legacy format (v1): raw gzip with ACOMM001 header ----
2416            let mut decoder = GzDecoder::new(&raw[..]);
2417            let mut data = Vec::new();
2418            decoder.read_to_end(&mut data)?;
2419
2420            // Deserialize legacy header first
2421            let header: AcommHeader = bincode::deserialize(&data)
2422                .map_err(|e| CommError::InvalidFile(format!("Bad header: {e}")))?;
2423
2424            if header.magic != *ACOMM_MAGIC {
2425                return Err(CommError::InvalidFile(
2426                    "Invalid magic bytes — not an .acomm file".to_string(),
2427                ));
2428            }
2429
2430            if header.version != ACOMM_VERSION {
2431                return Err(CommError::InvalidFile(format!(
2432                    "Unsupported version: {} (expected {})",
2433                    header.version, ACOMM_VERSION
2434                )));
2435            }
2436
2437            // Skip the header bytes to get to the store payload
2438            let header_size = bincode::serialized_size(&header)
2439                .map_err(|e| CommError::Serialization(e.to_string()))? as usize;
2440
2441            let store: CommStore = bincode::deserialize(&data[header_size..])
2442                .map_err(|e| CommError::InvalidFile(format!("Bad store data: {e}")))?;
2443
2444            // Lock released via Drop of `_lock`.
2445            Ok(store)
2446        }
2447    }
2448
2449    /// Get summary statistics for the store.
2450    pub fn stats(&self) -> CommStoreStats {
2451        // Count messages by type
2452        let mut messages_by_type: HashMap<String, usize> = HashMap::new();
2453        let mut messages_by_priority: HashMap<String, usize> = HashMap::new();
2454        let mut oldest_message: Option<DateTime<Utc>> = None;
2455        let mut newest_message: Option<DateTime<Utc>> = None;
2456
2457        for msg in self.messages.values() {
2458            *messages_by_type
2459                .entry(msg.message_type.to_string())
2460                .or_insert(0) += 1;
2461            *messages_by_priority
2462                .entry(msg.priority.to_string())
2463                .or_insert(0) += 1;
2464
2465            match oldest_message {
2466                None => oldest_message = Some(msg.timestamp),
2467                Some(ref ts) if msg.timestamp < *ts => oldest_message = Some(msg.timestamp),
2468                _ => {}
2469            }
2470            match newest_message {
2471                None => newest_message = Some(msg.timestamp),
2472                Some(ref ts) if msg.timestamp > *ts => newest_message = Some(msg.timestamp),
2473                _ => {}
2474            }
2475        }
2476
2477        // Count channels by state
2478        let mut channels_by_state: HashMap<String, usize> = HashMap::new();
2479        for ch in self.channels.values() {
2480            *channels_by_state
2481                .entry(ch.state.to_string())
2482                .or_insert(0) += 1;
2483        }
2484
2485        CommStoreStats {
2486            channel_count: self.channels.len(),
2487            message_count: self.messages.len(),
2488            subscription_count: self.subscriptions.len(),
2489            total_participants: self
2490                .channels
2491                .values()
2492                .map(|c| c.participants.len())
2493                .sum(),
2494            dead_letter_count: self.dead_letters.len(),
2495            messages_by_type,
2496            messages_by_priority,
2497            channels_by_state,
2498            oldest_message,
2499            newest_message,
2500            consent_gate_count: self.consent_gates.len(),
2501            trust_override_count: self.trust_levels.len(),
2502            temporal_queue_count: self.temporal_queue.iter().filter(|m| !m.delivered).count(),
2503            hive_count: self.hive_minds.len(),
2504            comm_log_count: self.comm_log.len(),
2505            federation_enabled: self.federation_config.enabled,
2506            federated_zone_count: self.federation_config.zones.len(),
2507            audit_log_count: self.audit_log.len(),
2508        }
2509    }
2510
2511    // -----------------------------------------------------------------------
2512    // Consent management
2513    // -----------------------------------------------------------------------
2514
2515    /// Grant consent from grantor to grantee for a specific scope.
2516    pub fn grant_consent(
2517        &mut self,
2518        grantor: &str,
2519        grantee: &str,
2520        scope: ConsentScope,
2521        reason: Option<String>,
2522        expires_at: Option<String>,
2523    ) -> CommResult<&ConsentGateEntry> {
2524        if grantor.is_empty() || grantee.is_empty() {
2525            return Err(CommError::ConsentError(
2526                "Grantor and grantee must be non-empty".to_string(),
2527            ));
2528        }
2529        let scope_str = scope.to_string();
2530        let now = Utc::now().to_rfc3339();
2531        // Check if an existing entry exists for this triple
2532        if let Some(entry) = self.consent_gates.iter_mut().find(|e| {
2533            e.grantor == grantor && e.grantee == grantee && e.scope == scope
2534        }) {
2535            entry.status = ConsentStatus::Granted;
2536            entry.updated_at = now;
2537            entry.reason = reason;
2538            entry.expires_at = expires_at;
2539            // Audit log
2540            self.audit_log.push(AuditEntry {
2541                event_type: AuditEventType::ConsentGranted,
2542                timestamp: Utc::now().to_rfc3339(),
2543                agent_id: grantor.to_string(),
2544                description: format!("Granted {} consent to '{}'", scope_str, grantee),
2545                related_id: Some(format!("{}->{}", grantor, grantee)),
2546            });
2547            let idx = self.consent_gates.iter().position(|e| {
2548                e.grantor == grantor && e.grantee == grantee && e.scope == scope
2549            }).unwrap();
2550            return Ok(&self.consent_gates[idx]);
2551        }
2552        // Create new entry
2553        let entry = ConsentGateEntry {
2554            grantor: grantor.to_string(),
2555            grantee: grantee.to_string(),
2556            scope,
2557            status: ConsentStatus::Granted,
2558            created_at: now.clone(),
2559            updated_at: now,
2560            expires_at,
2561            reason,
2562        };
2563        self.consent_gates.push(entry);
2564        // Audit log
2565        self.audit_log.push(AuditEntry {
2566            event_type: AuditEventType::ConsentGranted,
2567            timestamp: Utc::now().to_rfc3339(),
2568            agent_id: grantor.to_string(),
2569            description: format!("Granted {} consent to '{}'", scope_str, grantee),
2570            related_id: Some(format!("{}->{}", grantor, grantee)),
2571        });
2572        Ok(self.consent_gates.last().unwrap())
2573    }
2574
2575    /// Revoke consent.
2576    pub fn revoke_consent(
2577        &mut self,
2578        grantor: &str,
2579        grantee: &str,
2580        scope: &ConsentScope,
2581    ) -> CommResult<()> {
2582        if let Some(entry) = self.consent_gates.iter_mut().find(|e| {
2583            e.grantor == grantor && e.grantee == grantee && e.scope == *scope
2584        }) {
2585            entry.status = ConsentStatus::Revoked;
2586            entry.updated_at = Utc::now().to_rfc3339();
2587            // Audit log
2588            self.log_audit(
2589                AuditEventType::ConsentRevoked,
2590                grantor,
2591                &format!("Revoked {} consent from '{}'", scope, grantee),
2592                Some(format!("{}->{}", grantor, grantee)),
2593            );
2594            Ok(())
2595        } else {
2596            Err(CommError::ConsentError(format!(
2597                "No consent entry found for {grantor} -> {grantee} ({scope})"
2598            )))
2599        }
2600    }
2601
2602    /// Check if consent is granted.
2603    pub fn check_consent(
2604        &self,
2605        grantor: &str,
2606        grantee: &str,
2607        scope: &ConsentScope,
2608    ) -> bool {
2609        self.consent_gates.iter().any(|e| {
2610            e.grantor == grantor
2611                && e.grantee == grantee
2612                && e.scope == *scope
2613                && e.status == ConsentStatus::Granted
2614        })
2615    }
2616
2617    /// List all consent gates, optionally filtered by agent.
2618    pub fn list_consent_gates(&self, agent: Option<&str>) -> Vec<&ConsentGateEntry> {
2619        self.consent_gates
2620            .iter()
2621            .filter(|e| {
2622                agent.map_or(true, |a| e.grantor == a || e.grantee == a)
2623            })
2624            .collect()
2625    }
2626
2627    // -----------------------------------------------------------------------
2628    // Trust management
2629    // -----------------------------------------------------------------------
2630
2631    /// Set trust level for an agent.
2632    pub fn set_trust_level(
2633        &mut self,
2634        agent_id: &str,
2635        level: CommTrustLevel,
2636    ) -> CommResult<()> {
2637        if agent_id.is_empty() {
2638            return Err(CommError::TrustError(
2639                "Agent ID must be non-empty".to_string(),
2640            ));
2641        }
2642        self.trust_levels.insert(agent_id.to_string(), level);
2643
2644        // --- Audit logging ---
2645        self.log_audit(
2646            AuditEventType::TrustUpdated,
2647            agent_id,
2648            &format!("Trust level set to {} for '{}'", level, agent_id),
2649            Some(agent_id.to_string()),
2650        );
2651
2652        Ok(())
2653    }
2654
2655    /// Get trust level for an agent (default: Standard).
2656    pub fn get_trust_level(&self, agent_id: &str) -> CommTrustLevel {
2657        self.trust_levels
2658            .get(agent_id)
2659            .copied()
2660            .unwrap_or(CommTrustLevel::Standard)
2661    }
2662
2663    /// List all trust level overrides.
2664    pub fn list_trust_levels(&self) -> &HashMap<String, CommTrustLevel> {
2665        &self.trust_levels
2666    }
2667
2668    // -----------------------------------------------------------------------
2669    // Temporal scheduling
2670    // -----------------------------------------------------------------------
2671
2672    /// Schedule a message for future delivery.
2673    pub fn schedule_message(
2674        &mut self,
2675        channel_id: u64,
2676        sender: &str,
2677        content: &str,
2678        target: TemporalTarget,
2679        affect: Option<AffectState>,
2680    ) -> CommResult<&TemporalMessage> {
2681        // Validate channel exists
2682        if !self.channels.contains_key(&channel_id) {
2683            return Err(CommError::ChannelNotFound(channel_id));
2684        }
2685        Self::validate_sender(sender)?;
2686        Self::validate_content(content)?;
2687
2688        // --- Consent enforcement ---
2689        if !self.check_consent_for_action(sender, "temporal", ConsentScope::ScheduleMessages) {
2690            return Err(CommError::ConsentDenied {
2691                reason: "Consent not granted for scheduling messages".to_string(),
2692            });
2693        }
2694
2695        let id = self.next_temporal_id;
2696        self.next_temporal_id += 1;
2697
2698        let msg = TemporalMessage {
2699            id,
2700            channel_id,
2701            sender: sender.to_string(),
2702            content: content.to_string(),
2703            target,
2704            scheduled_at: Utc::now().to_rfc3339(),
2705            delivered: false,
2706            affect,
2707        };
2708        self.temporal_queue.push(msg);
2709
2710        // Bridge point: time_bridge.schedule_at() for precise timing
2711
2712        // --- Audit logging ---
2713        self.audit_log.push(AuditEntry {
2714            event_type: AuditEventType::ScheduledMessage,
2715            timestamp: Utc::now().to_rfc3339(),
2716            agent_id: sender.to_string(),
2717            description: format!(
2718                "Scheduled message to channel {} (temporal_id={})",
2719                channel_id, id
2720            ),
2721            related_id: Some(id.to_string()),
2722        });
2723
2724        Ok(self.temporal_queue.last().unwrap())
2725    }
2726
2727    /// List all scheduled (undelivered) temporal messages.
2728    pub fn list_scheduled(&self) -> Vec<&TemporalMessage> {
2729        self.temporal_queue
2730            .iter()
2731            .filter(|m| !m.delivered)
2732            .collect()
2733    }
2734
2735    /// Cancel a scheduled message.
2736    pub fn cancel_scheduled(&mut self, temporal_id: u64) -> CommResult<()> {
2737        if let Some(msg) = self.temporal_queue.iter_mut().find(|m| m.id == temporal_id) {
2738            if msg.delivered {
2739                return Err(CommError::TemporalError(
2740                    "Cannot cancel already-delivered message".to_string(),
2741                ));
2742            }
2743            self.temporal_queue.retain(|m| m.id != temporal_id);
2744            Ok(())
2745        } else {
2746            Err(CommError::TemporalError(format!(
2747                "Scheduled message {temporal_id} not found"
2748            )))
2749        }
2750    }
2751
2752    /// Deliver all pending temporal messages that are due (Immediate targets).
2753    /// Returns the number of messages delivered.
2754    pub fn deliver_pending_temporal(&mut self) -> usize {
2755        let mut delivered = 0;
2756        let mut to_deliver = Vec::new();
2757
2758        for msg in &self.temporal_queue {
2759            if msg.delivered {
2760                continue;
2761            }
2762            match &msg.target {
2763                TemporalTarget::Immediate => {
2764                    to_deliver.push((msg.id, msg.channel_id, msg.sender.clone(), msg.content.clone()));
2765                }
2766                _ => {} // Other targets need time/condition checking
2767            }
2768        }
2769
2770        for (temporal_id, channel_id, sender, content) in to_deliver {
2771            if self.send_message(channel_id, &sender, &content, MessageType::Text).is_ok() {
2772                if let Some(msg) = self.temporal_queue.iter_mut().find(|m| m.id == temporal_id) {
2773                    msg.delivered = true;
2774                }
2775                delivered += 1;
2776            }
2777        }
2778        delivered
2779    }
2780
2781    // -----------------------------------------------------------------------
2782    // Affect messaging
2783    // -----------------------------------------------------------------------
2784
2785    /// Send a message with affect/emotional context.
2786    pub fn send_affect_message(
2787        &mut self,
2788        channel_id: u64,
2789        sender: &str,
2790        content: &str,
2791        affect: AffectState,
2792    ) -> CommResult<Message> {
2793        // Validate
2794        Self::validate_sender(sender)?;
2795        Self::validate_content(content)?;
2796        self.check_channel_allows_send(channel_id)?;
2797
2798        // Embed affect as JSON prefix in content for storage
2799        let affect_json = serde_json::to_string(&affect)
2800            .map_err(|e| CommError::Serialization(e.to_string()))?;
2801        let enriched = format!("[affect:{}]{}", affect_json, content);
2802
2803        self.send_message(channel_id, sender, &enriched, MessageType::Text)
2804    }
2805
2806    // -----------------------------------------------------------------------
2807    // Federation management
2808    // -----------------------------------------------------------------------
2809
2810    /// Configure federation settings.
2811    pub fn configure_federation(
2812        &mut self,
2813        enabled: bool,
2814        local_zone: &str,
2815        default_policy: FederationPolicy,
2816    ) -> CommResult<()> {
2817        if local_zone.is_empty() {
2818            return Err(CommError::FederationError(
2819                "Local zone must be non-empty".to_string(),
2820            ));
2821        }
2822
2823        // --- Consent enforcement ---
2824        // If any Federate consent gates exist, the configuring agent (system)
2825        // must have an explicit grant.
2826        if !self.check_consent_for_action("system", local_zone, ConsentScope::Federate) {
2827            return Err(CommError::ConsentDenied {
2828                reason: "Consent not granted for federation".to_string(),
2829            });
2830        }
2831
2832        self.federation_config.enabled = enabled;
2833        self.federation_config.local_zone = local_zone.to_string();
2834        self.federation_config.default_policy = default_policy;
2835
2836        // --- Audit logging ---
2837        self.log_audit(
2838            AuditEventType::FederationConfigured,
2839            "system",
2840            &format!(
2841                "Federation configured: enabled={}, zone='{}', policy={}",
2842                enabled, local_zone, default_policy
2843            ),
2844            Some(local_zone.to_string()),
2845        );
2846
2847        Ok(())
2848    }
2849
2850    /// Get current federation configuration.
2851    pub fn get_federation_config(&self) -> &FederationConfig {
2852        &self.federation_config
2853    }
2854
2855    /// Add a federated zone.
2856    pub fn add_federated_zone(&mut self, zone: FederatedZone) -> CommResult<()> {
2857        if zone.zone_id.is_empty() {
2858            return Err(CommError::FederationError(
2859                "Zone ID must be non-empty".to_string(),
2860            ));
2861        }
2862        // Check for duplicates
2863        if self.federation_config.zones.iter().any(|z| z.zone_id == zone.zone_id) {
2864            return Err(CommError::FederationError(format!(
2865                "Zone '{}' already exists", zone.zone_id
2866            )));
2867        }
2868        self.federation_config.zones.push(zone);
2869        Ok(())
2870    }
2871
2872    /// Remove a federated zone.
2873    pub fn remove_federated_zone(&mut self, zone_id: &str) -> CommResult<()> {
2874        let before = self.federation_config.zones.len();
2875        self.federation_config.zones.retain(|z| z.zone_id != zone_id);
2876        if self.federation_config.zones.len() == before {
2877            return Err(CommError::FederationError(format!(
2878                "Zone '{zone_id}' not found"
2879            )));
2880        }
2881        Ok(())
2882    }
2883
2884    /// List all federated zones.
2885    pub fn list_federated_zones(&self) -> &[FederatedZone] {
2886        &self.federation_config.zones
2887    }
2888
2889    // -----------------------------------------------------------------------
2890    // Hive mind management
2891    // -----------------------------------------------------------------------
2892
2893    /// Form a new hive mind.
2894    pub fn form_hive(
2895        &mut self,
2896        name: &str,
2897        coordinator: &str,
2898        decision_mode: CollectiveDecisionMode,
2899    ) -> CommResult<&HiveMind> {
2900        if name.is_empty() {
2901            return Err(CommError::HiveError(
2902                "Hive name must be non-empty".to_string(),
2903            ));
2904        }
2905        if coordinator.is_empty() {
2906            return Err(CommError::HiveError(
2907                "Coordinator must be non-empty".to_string(),
2908            ));
2909        }
2910
2911        // --- Consent enforcement ---
2912        if !self.check_consent_for_action(coordinator, name, ConsentScope::HiveParticipation) {
2913            return Err(CommError::ConsentDenied {
2914                reason: "Consent not granted for hive participation".to_string(),
2915            });
2916        }
2917
2918        let id = self.next_hive_id;
2919        self.next_hive_id += 1;
2920        let now = Utc::now().to_rfc3339();
2921        let hive = HiveMind {
2922            id,
2923            name: name.to_string(),
2924            constituents: vec![HiveConstituent {
2925                agent_id: coordinator.to_string(),
2926                role: HiveRole::Coordinator,
2927                joined_at: now.clone(),
2928            }],
2929            decision_mode,
2930            formed_at: now,
2931            metadata: HashMap::new(),
2932            coherence_level: 1.0,
2933            separation_policy: "graceful".to_string(),
2934            cognitive_space: None,
2935        };
2936        self.hive_minds.insert(id, hive);
2937
2938        // Bridge point: contract_bridge.validate_channel_contract() for SLA enforcement
2939
2940        // --- Audit logging ---
2941        self.audit_log.push(AuditEntry {
2942            event_type: AuditEventType::HiveFormed,
2943            timestamp: Utc::now().to_rfc3339(),
2944            agent_id: coordinator.to_string(),
2945            description: format!("Formed hive '{}' (id={})", name, id),
2946            related_id: Some(id.to_string()),
2947        });
2948
2949        Ok(self.hive_minds.get(&id).unwrap())
2950    }
2951
2952    /// Dissolve a hive mind.
2953    pub fn dissolve_hive(&mut self, hive_id: u64) -> CommResult<()> {
2954        if self.hive_minds.remove(&hive_id).is_none() {
2955            return Err(CommError::HiveError(format!(
2956                "Hive {hive_id} not found"
2957            )));
2958        }
2959
2960        // --- Audit logging ---
2961        self.log_audit(
2962            AuditEventType::HiveDissolved,
2963            "system",
2964            &format!("Dissolved hive (id={})", hive_id),
2965            Some(hive_id.to_string()),
2966        );
2967
2968        Ok(())
2969    }
2970
2971    /// Join a hive mind.
2972    pub fn join_hive(
2973        &mut self,
2974        hive_id: u64,
2975        agent_id: &str,
2976        role: HiveRole,
2977    ) -> CommResult<()> {
2978        // --- Consent enforcement (checked before mutable borrow) ---
2979        {
2980            let hive_name = self
2981                .hive_minds
2982                .get(&hive_id)
2983                .map(|h| h.name.clone())
2984                .unwrap_or_default();
2985            if !self.check_consent_for_action(agent_id, &hive_name, ConsentScope::HiveParticipation) {
2986                return Err(CommError::ConsentDenied {
2987                    reason: "Consent not granted for hive participation".to_string(),
2988                });
2989            }
2990        }
2991
2992        let hive = self
2993            .hive_minds
2994            .get_mut(&hive_id)
2995            .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
2996
2997        if hive.constituents.iter().any(|c| c.agent_id == agent_id) {
2998            return Err(CommError::HiveError(format!(
2999                "Agent '{agent_id}' is already in hive {hive_id}"
3000            )));
3001        }
3002
3003        hive.constituents.push(HiveConstituent {
3004            agent_id: agent_id.to_string(),
3005            role,
3006            joined_at: Utc::now().to_rfc3339(),
3007        });
3008        Ok(())
3009    }
3010
3011    /// Leave a hive mind.
3012    pub fn leave_hive(&mut self, hive_id: u64, agent_id: &str) -> CommResult<()> {
3013        let hive = self
3014            .hive_minds
3015            .get_mut(&hive_id)
3016            .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
3017
3018        let before = hive.constituents.len();
3019        hive.constituents.retain(|c| c.agent_id != agent_id);
3020        if hive.constituents.len() == before {
3021            return Err(CommError::HiveError(format!(
3022                "Agent '{agent_id}' is not in hive {hive_id}"
3023            )));
3024        }
3025        Ok(())
3026    }
3027
3028    /// List all hive minds.
3029    pub fn list_hives(&self) -> Vec<&HiveMind> {
3030        self.hive_minds.values().collect()
3031    }
3032
3033    /// Get a specific hive mind.
3034    pub fn get_hive(&self, hive_id: u64) -> Option<&HiveMind> {
3035        self.hive_minds.get(&hive_id)
3036    }
3037
3038    // -----------------------------------------------------------------------
3039    // Communication log (mirrors memory's conversation_log)
3040    // -----------------------------------------------------------------------
3041
3042    /// Log a communication context entry.
3043    pub fn log_communication(
3044        &mut self,
3045        content: &str,
3046        role: &str,
3047        topic: Option<String>,
3048        linked_message_id: Option<u64>,
3049        affect: Option<AffectState>,
3050    ) -> &CommunicationLogEntry {
3051        let idx = self.next_log_index;
3052        self.next_log_index += 1;
3053        let entry = CommunicationLogEntry {
3054            index: idx,
3055            content: content.to_string(),
3056            role: role.to_string(),
3057            topic,
3058            timestamp: Utc::now().to_rfc3339(),
3059            linked_message_id,
3060            affect,
3061        };
3062        self.comm_log.push(entry);
3063        self.comm_log.last().unwrap()
3064    }
3065
3066    /// Get communication log entries.
3067    pub fn get_comm_log(&self, limit: Option<usize>) -> &[CommunicationLogEntry] {
3068        match limit {
3069            Some(n) if n < self.comm_log.len() => &self.comm_log[self.comm_log.len() - n..],
3070            _ => &self.comm_log,
3071        }
3072    }
3073
3074    // -----------------------------------------------------------------------
3075    // Audit log
3076    // -----------------------------------------------------------------------
3077
3078    /// Log an audit event.
3079    pub fn log_audit(
3080        &mut self,
3081        event_type: AuditEventType,
3082        agent_id: &str,
3083        description: &str,
3084        related_id: Option<String>,
3085    ) {
3086        let entry = AuditEntry {
3087            event_type,
3088            timestamp: Utc::now().to_rfc3339(),
3089            agent_id: agent_id.to_string(),
3090            description: description.to_string(),
3091            related_id,
3092        };
3093        self.audit_log.push(entry);
3094    }
3095
3096    /// Get recent audit log entries.
3097    pub fn get_audit_log(&self, limit: Option<usize>) -> Vec<&AuditEntry> {
3098        match limit {
3099            Some(n) if n < self.audit_log.len() => {
3100                self.audit_log[self.audit_log.len() - n..].iter().collect()
3101            }
3102            _ => self.audit_log.iter().collect(),
3103        }
3104    }
3105
3106    /// Rotate audit log, keeping only the most recent entries.
3107    pub fn rotate_audit_log(&mut self, max_entries: usize) -> usize {
3108        if self.audit_log.len() <= max_entries {
3109            return 0;
3110        }
3111        let removed = self.audit_log.len() - max_entries;
3112        self.audit_log = self.audit_log.split_off(removed);
3113        removed
3114    }
3115
3116    /// Enforce retention policy, removing entries older than cutoff timestamp.
3117    pub fn enforce_audit_retention(&mut self, cutoff_timestamp: &str) -> usize {
3118        let before = self.audit_log.len();
3119        self.audit_log.retain(|entry| entry.timestamp.as_str() >= cutoff_timestamp);
3120        before - self.audit_log.len()
3121    }
3122
3123    /// Export audit log as JSON array.
3124    pub fn export_audit_log(&self) -> serde_json::Value {
3125        let entries: Vec<serde_json::Value> = self.audit_log.iter().map(|e| {
3126            serde_json::json!({
3127                "event_type": format!("{:?}", e.event_type),
3128                "timestamp": e.timestamp,
3129                "agent_id": e.agent_id,
3130                "description": e.description,
3131                "related_id": e.related_id,
3132            })
3133        }).collect();
3134        serde_json::json!({
3135            "total_entries": entries.len(),
3136            "entries": entries,
3137        })
3138    }
3139
3140    // -----------------------------------------------------------------------
3141    // Semantic operations
3142    // -----------------------------------------------------------------------
3143
3144    /// Send a semantic message (structured meaning payload).
3145    pub fn send_semantic(
3146        &mut self,
3147        channel_id: u64,
3148        sender: &str,
3149        topic: &str,
3150        focus_nodes: Vec<String>,
3151        depth: u64,
3152    ) -> CommResult<SemanticOperation> {
3153        // Verify channel exists
3154        if !self.channels.contains_key(&channel_id) {
3155            return Err(CommError::ChannelNotFound(channel_id));
3156        }
3157        let id = self.next_semantic_id;
3158        self.next_semantic_id += 1;
3159        let op = SemanticOperation {
3160            id,
3161            topic: topic.to_string(),
3162            focus_nodes,
3163            depth,
3164            timestamp: Utc::now().timestamp() as u64,
3165            operation: "send".to_string(),
3166            channel_id: Some(channel_id),
3167            sender: Some(sender.to_string()),
3168        };
3169        self.semantic_operations.push(op.clone());
3170        Ok(op)
3171    }
3172
3173    /// Extract semantics from a message.
3174    pub fn extract_semantic(&self, message_id: u64) -> CommResult<SemanticOperation> {
3175        let msg = self
3176            .messages
3177            .get(&message_id)
3178            .ok_or(CommError::MessageNotFound(message_id))?;
3179        Ok(SemanticOperation {
3180            id: 0,
3181            topic: String::new(),
3182            focus_nodes: vec![],
3183            depth: 1,
3184            timestamp: Utc::now().timestamp() as u64,
3185            operation: "extract".to_string(),
3186            channel_id: Some(msg.channel_id),
3187            sender: Some(msg.sender.clone()),
3188        })
3189    }
3190
3191    /// Graft (merge) semantic layers.
3192    pub fn graft_semantic(
3193        &mut self,
3194        source_id: u64,
3195        target_id: u64,
3196        strategy: &str,
3197    ) -> CommResult<SemanticOperation> {
3198        let _ = (source_id, target_id, strategy);
3199        let id = self.next_semantic_id;
3200        self.next_semantic_id += 1;
3201        let op = SemanticOperation {
3202            id,
3203            topic: String::new(),
3204            focus_nodes: vec![],
3205            depth: 1,
3206            timestamp: Utc::now().timestamp() as u64,
3207            operation: format!("graft:{}->{}:{}", source_id, target_id, strategy),
3208            channel_id: None,
3209            sender: None,
3210        };
3211        self.semantic_operations.push(op.clone());
3212        Ok(op)
3213    }
3214
3215    /// List semantic conflicts.
3216    pub fn list_semantic_conflicts(
3217        &self,
3218        channel_id: Option<u64>,
3219        severity: Option<&str>,
3220    ) -> Vec<&SemanticConflict> {
3221        self.semantic_conflicts
3222            .iter()
3223            .filter(|c| {
3224                channel_id.map_or(true, |cid| c.channel_id == Some(cid))
3225                    && severity.map_or(true, |s| c.severity == s)
3226            })
3227            .collect()
3228    }
3229
3230    // -----------------------------------------------------------------------
3231    // Affect queries
3232    // -----------------------------------------------------------------------
3233
3234    /// Get the current affect state for an agent.
3235    pub fn get_affect_state(&self, agent_id: &str) -> Option<&AffectState> {
3236        self.affect_states.get(agent_id)
3237    }
3238
3239    /// Set the affect resistance threshold.
3240    pub fn set_affect_resistance(&mut self, resistance: f64) -> f64 {
3241        let clamped = resistance.clamp(0.0, 1.0);
3242        self.affect_resistance = clamped;
3243        clamped
3244    }
3245
3246
3247    // -----------------------------------------------------------------------
3248    // Affect contagion pipeline
3249    // -----------------------------------------------------------------------
3250
3251    /// Process affect contagion across all participants in a channel.
3252    ///
3253    /// For each message with affect metadata (valence, arousal, dominance),
3254    /// apply a simple contagion model: each receiver's state is nudged toward
3255    /// the sender's state, weighted by `(1 - affect_resistance)`.
3256    pub fn process_affect_contagion(
3257        &mut self,
3258        channel_id: u64,
3259    ) -> Vec<(String, f64, f64, f64)> {
3260        let channel = match self.channels.get(&channel_id) {
3261            Some(ch) => ch.clone(),
3262            None => return Vec::new(),
3263        };
3264        let participants = channel.participants.clone();
3265        let resistance = self.affect_resistance;
3266
3267        // Collect messages with affect metadata
3268        let mut affect_messages: Vec<(String, f64, f64, f64)> = Vec::new();
3269        for msg in self.messages.values() {
3270            if msg.channel_id != channel_id {
3271                continue;
3272            }
3273            let valence = msg
3274                .metadata
3275                .get("valence")
3276                .and_then(|v| v.parse::<f64>().ok());
3277            let arousal = msg
3278                .metadata
3279                .get("arousal")
3280                .and_then(|v| v.parse::<f64>().ok());
3281            let dominance = msg
3282                .metadata
3283                .get("dominance")
3284                .and_then(|v| v.parse::<f64>().ok());
3285
3286            if let (Some(v), Some(a), Some(d)) = (valence, arousal, dominance) {
3287                affect_messages.push((msg.sender.clone(), v, a, d));
3288            }
3289        }
3290
3291        let mut results: Vec<(String, f64, f64, f64)> = Vec::new();
3292
3293        for (sender, v, a, d) in &affect_messages {
3294            for participant in &participants {
3295                if participant == sender {
3296                    continue;
3297                }
3298                let weight = 1.0 - resistance;
3299                let current = self
3300                    .affect_states
3301                    .get(participant)
3302                    .cloned()
3303                    .unwrap_or_default();
3304                let new_valence =
3305                    (current.valence + (v - current.valence) * weight).clamp(-1.0, 1.0);
3306                let new_arousal =
3307                    (current.arousal + (a - current.arousal) * weight).clamp(0.0, 1.0);
3308                let new_dominance =
3309                    (current.dominance + (d - current.dominance) * weight).clamp(0.0, 1.0);
3310
3311                let state = self
3312                    .affect_states
3313                    .entry(participant.clone())
3314                    .or_insert_with(AffectState::default);
3315                state.valence = new_valence;
3316                state.arousal = new_arousal;
3317                state.dominance = new_dominance;
3318
3319                results.push((
3320                    participant.clone(),
3321                    new_valence,
3322                    new_arousal,
3323                    new_dominance,
3324                ));
3325            }
3326        }
3327
3328        results
3329    }
3330
3331    /// Retrieve the full affect history for an agent.
3332    ///
3333    /// Builds a history from the current affect state and any messages
3334    /// sent by or to the agent that carried affect metadata.
3335    pub fn get_affect_history(&self, agent: &str) -> types::AffectHistory {
3336        use crate::types::{AffectHistory, AffectHistoryEntry};
3337
3338        let mut entries: Vec<AffectHistoryEntry> = Vec::new();
3339
3340        // Scan messages for affect metadata involving this agent
3341        for msg in self.messages.values() {
3342            let involves_agent = msg.sender == agent
3343                || msg
3344                    .recipient
3345                    .as_deref()
3346                    .map_or(false, |r| r == agent);
3347
3348            if !involves_agent {
3349                continue;
3350            }
3351
3352            let valence = msg
3353                .metadata
3354                .get("valence")
3355                .and_then(|v| v.parse::<f64>().ok());
3356            let arousal = msg
3357                .metadata
3358                .get("arousal")
3359                .and_then(|v| v.parse::<f64>().ok());
3360            let dominance = msg
3361                .metadata
3362                .get("dominance")
3363                .and_then(|v| v.parse::<f64>().ok());
3364
3365            if valence.is_some() || arousal.is_some() || dominance.is_some() {
3366                entries.push(AffectHistoryEntry {
3367                    timestamp: msg.timestamp.timestamp() as u64,
3368                    emotion: String::new(),
3369                    intensity: 0.0,
3370                    valence: valence.unwrap_or(0.0),
3371                    arousal: arousal.unwrap_or(0.0),
3372                    dominance: dominance.unwrap_or(0.5),
3373                    source: if msg.sender == agent {
3374                        "direct".to_string()
3375                    } else {
3376                        "contagion".to_string()
3377                    },
3378                });
3379            }
3380        }
3381
3382        // Add current state if it exists
3383        if let Some(state) = self.affect_states.get(agent) {
3384            entries.push(AffectHistoryEntry {
3385                timestamp: chrono::Utc::now().timestamp() as u64,
3386                emotion: String::new(),
3387                intensity: 0.0,
3388                valence: state.valence,
3389                arousal: state.arousal,
3390                dominance: state.dominance,
3391                source: "current".to_string(),
3392            });
3393        }
3394
3395        entries.sort_by_key(|e| e.timestamp);
3396
3397        AffectHistory {
3398            agent: agent.to_string(),
3399            states: entries,
3400        }
3401    }
3402
3403    /// Apply temporal decay to all agent affect states.
3404    ///
3405    /// Each dimension is multiplied by `(1.0 - decay_rate)`, then clamped
3406    /// to valid ranges: valence [-1.0, 1.0], arousal [0.0, 1.0],
3407    /// dominance [0.0, 1.0].
3408    pub fn apply_affect_decay(&mut self, decay_rate: f64) {
3409        let factor = 1.0 - decay_rate.clamp(0.0, 1.0);
3410        for state in self.affect_states.values_mut() {
3411            state.valence = (state.valence * factor).clamp(-1.0, 1.0);
3412            state.arousal = (state.arousal * factor).clamp(0.0, 1.0);
3413            state.dominance = (state.dominance * factor).clamp(0.0, 1.0);
3414        }
3415    }
3416
3417    // -----------------------------------------------------------------------
3418    // Message forwarding with echo tracking
3419    // -----------------------------------------------------------------------
3420
3421    /// Forward a message to another channel with echo tracking metadata.
3422    ///
3423    /// Creates a new message in `target_channel` with content prefixed
3424    /// "[Forwarded] " and metadata tracking the forwarding chain.
3425    pub fn forward_message(
3426        &mut self,
3427        original_id: u64,
3428        target_channel: u64,
3429        forwarder: &str,
3430    ) -> Result<u64, String> {
3431        let original = self
3432            .messages
3433            .get(&original_id)
3434            .cloned()
3435            .ok_or_else(|| format!("Message {} not found", original_id))?;
3436
3437        if !self.channels.contains_key(&target_channel) {
3438            return Err(format!("Target channel {} not found", target_channel));
3439        }
3440
3441        // Determine echo depth and original root
3442        let parent_depth: u32 = original
3443            .metadata
3444            .get("echo_depth")
3445            .and_then(|v| v.parse().ok())
3446            .unwrap_or(0);
3447        let root_id = original
3448            .metadata
3449            .get("original_message_id")
3450            .and_then(|v| v.parse::<u64>().ok())
3451            .unwrap_or(original_id);
3452
3453        let content = format!("[Forwarded] {}", original.content);
3454
3455        let id = self.next_message_id;
3456        self.next_message_id += 1;
3457
3458        self.lamport_counter += 1;
3459        let mut ts = types::CommTimestamp::now(forwarder);
3460        ts.lamport = self.lamport_counter;
3461
3462        let mut metadata = HashMap::new();
3463        metadata.insert("forwarded_from".to_string(), original_id.to_string());
3464        metadata.insert("echo_depth".to_string(), (parent_depth + 1).to_string());
3465        metadata.insert("original_message_id".to_string(), root_id.to_string());
3466        metadata.insert("forwarder".to_string(), forwarder.to_string());
3467
3468        let message = Message {
3469            id,
3470            channel_id: target_channel,
3471            sender: forwarder.to_string(),
3472            recipient: None,
3473            content,
3474            message_type: MessageType::Text,
3475            timestamp: Utc::now(),
3476            metadata,
3477            signature: Some(self.compute_signature(&original.content)),
3478            acknowledged_by: Vec::new(),
3479            status: MessageStatus::Sent,
3480            priority: MessagePriority::default(),
3481            reply_to: None,
3482            correlation_id: None,
3483            thread_id: None,
3484            comm_timestamp: ts,
3485            rich_content_json: None,
3486            comm_id: None,
3487            receipt_id: None,
3488        };
3489
3490        self.messages.insert(id, message);
3491
3492        self.log_audit(
3493            AuditEventType::MessageSent,
3494            forwarder,
3495            &format!(
3496                "Forwarded message {} to channel {} (depth {})",
3497                original_id,
3498                target_channel,
3499                parent_depth + 1
3500            ),
3501            Some(id.to_string()),
3502        );
3503
3504        Ok(id)
3505    }
3506
3507    /// Trace the full forwarding (echo) chain of a message.
3508    ///
3509    /// Follows "forwarded_from" metadata backwards to the root, then
3510    /// searches forward for all messages forwarded from any message in the
3511    /// chain.
3512    pub fn query_echo_chain(&self, message_id: u64) -> Vec<types::EchoChainEntry> {
3513        use crate::types::EchoChainEntry;
3514
3515        let mut chain: Vec<EchoChainEntry> = Vec::new();
3516
3517        // Walk backwards to the root
3518        let mut current_id = message_id;
3519        let mut visited: std::collections::HashSet<u64> = std::collections::HashSet::new();
3520        loop {
3521            if !visited.insert(current_id) {
3522                break; // cycle protection
3523            }
3524            let msg = match self.messages.get(&current_id) {
3525                Some(m) => m,
3526                None => break,
3527            };
3528            let depth: u32 = msg
3529                .metadata
3530                .get("echo_depth")
3531                .and_then(|v| v.parse().ok())
3532                .unwrap_or(0);
3533            let forwarder = msg
3534                .metadata
3535                .get("forwarder")
3536                .cloned()
3537                .unwrap_or_else(|| msg.sender.clone());
3538
3539            chain.push(EchoChainEntry {
3540                message_id: current_id,
3541                channel_id: msg.channel_id,
3542                sender: msg.sender.clone(),
3543                forwarder,
3544                depth,
3545                timestamp: msg.timestamp.timestamp() as u64,
3546            });
3547
3548            match msg
3549                .metadata
3550                .get("forwarded_from")
3551                .and_then(|v| v.parse::<u64>().ok())
3552            {
3553                Some(parent) => current_id = parent,
3554                None => break,
3555            }
3556        }
3557
3558        chain.reverse(); // root first
3559
3560        // Walk forward: find all messages forwarded from any message in the chain
3561        let chain_ids: std::collections::HashSet<u64> =
3562            chain.iter().map(|e| e.message_id).collect();
3563        for msg in self.messages.values() {
3564            if chain_ids.contains(&msg.id) {
3565                continue; // already in chain
3566            }
3567            if let Some(parent_str) = msg.metadata.get("forwarded_from") {
3568                if let Ok(parent_id) = parent_str.parse::<u64>() {
3569                    if chain_ids.contains(&parent_id) {
3570                        let depth: u32 = msg
3571                            .metadata
3572                            .get("echo_depth")
3573                            .and_then(|v| v.parse().ok())
3574                            .unwrap_or(0);
3575                        let forwarder = msg
3576                            .metadata
3577                            .get("forwarder")
3578                            .cloned()
3579                            .unwrap_or_else(|| msg.sender.clone());
3580                        chain.push(EchoChainEntry {
3581                            message_id: msg.id,
3582                            channel_id: msg.channel_id,
3583                            sender: msg.sender.clone(),
3584                            forwarder,
3585                            depth,
3586                            timestamp: msg.timestamp.timestamp() as u64,
3587                        });
3588                    }
3589                }
3590            }
3591        }
3592
3593        chain.sort_by_key(|e| (e.depth, e.timestamp));
3594        chain
3595    }
3596
3597    /// Get the forwarding depth of a message in its echo chain.
3598    ///
3599    /// Returns the "echo_depth" metadata value, or 0 if the message is an
3600    /// original (not forwarded).
3601    pub fn get_echo_depth(&self, message_id: u64) -> u32 {
3602        self.messages
3603            .get(&message_id)
3604            .and_then(|msg| {
3605                msg.metadata
3606                    .get("echo_depth")
3607                    .and_then(|v| v.parse().ok())
3608            })
3609            .unwrap_or(0)
3610    }
3611
3612    // -----------------------------------------------------------------------
3613    // Conversation summarization
3614    // -----------------------------------------------------------------------
3615
3616    /// Generate detailed conversation statistics for a channel.
3617    pub fn summarize_conversation(
3618        &self,
3619        channel_id: u64,
3620    ) -> Result<types::ConversationSummaryDetailed, String> {
3621        use crate::types::ConversationSummaryDetailed;
3622
3623        let channel = self
3624            .channels
3625            .get(&channel_id)
3626            .ok_or_else(|| format!("Channel {} not found", channel_id))?;
3627
3628        let msgs: Vec<&Message> = self
3629            .messages
3630            .values()
3631            .filter(|m| m.channel_id == channel_id)
3632            .collect();
3633
3634        let message_count = msgs.len();
3635        let participants = channel.participants.clone();
3636        let participant_count = participants.len();
3637
3638        let first_message_time = msgs
3639            .iter()
3640            .map(|m| m.timestamp.timestamp() as u64)
3641            .min();
3642        let last_message_time = msgs
3643            .iter()
3644            .map(|m| m.timestamp.timestamp() as u64)
3645            .max();
3646
3647        let duration_secs = match (first_message_time, last_message_time) {
3648            (Some(first), Some(last)) if last > first => last - first,
3649            _ => 0,
3650        };
3651
3652        let messages_per_minute = if duration_secs > 0 {
3653            (message_count as f64) / (duration_secs as f64 / 60.0)
3654        } else if message_count > 0 {
3655            message_count as f64
3656        } else {
3657            0.0
3658        };
3659
3660        // Count messages per sender
3661        let mut sender_counts: HashMap<String, usize> = HashMap::new();
3662        for msg in &msgs {
3663            *sender_counts
3664                .entry(msg.sender.clone())
3665                .or_insert(0) += 1;
3666        }
3667        let mut top_senders: Vec<(String, usize)> = sender_counts.into_iter().collect();
3668        top_senders.sort_by(|a, b| b.1.cmp(&a.1));
3669
3670        let most_active_participant = top_senders.first().map(|(name, _)| name.clone());
3671        let most_active_count = top_senders.first().map(|(_, c)| *c).unwrap_or(0);
3672
3673        // Thread count
3674        let thread_ids: std::collections::HashSet<&str> = msgs
3675            .iter()
3676            .filter_map(|m| m.thread_id.as_deref())
3677            .collect();
3678        let thread_count = thread_ids.len();
3679
3680        // Reply count
3681        let reply_count = msgs.iter().filter(|m| m.reply_to.is_some()).count();
3682
3683        // Average message length
3684        let avg_message_length = if message_count > 0 {
3685            msgs.iter().map(|m| m.content.len()).sum::<usize>() as f64 / message_count as f64
3686        } else {
3687            0.0
3688        };
3689
3690        // Check for affect data
3691        let has_affect_data = msgs.iter().any(|m| m.metadata.contains_key("valence"));
3692
3693        Ok(ConversationSummaryDetailed {
3694            channel_id,
3695            channel_name: channel.name.clone(),
3696            participant_count,
3697            message_count,
3698            participants,
3699            first_message_time,
3700            last_message_time,
3701            duration_secs,
3702            messages_per_minute,
3703            top_senders,
3704            most_active_participant,
3705            most_active_count,
3706            avg_message_length,
3707            thread_count,
3708            reply_count,
3709            has_affect_data,
3710        })
3711    }
3712
3713    // -----------------------------------------------------------------------
3714    // Hive extensions
3715    // -----------------------------------------------------------------------
3716
3717    /// Broadcast a question to all hive members and return aggregated response.
3718    pub fn hive_think(
3719        &self,
3720        hive_id: u64,
3721        question: &str,
3722        timeout_ms: u64,
3723    ) -> CommResult<serde_json::Value> {
3724        let hive = self
3725            .hive_minds
3726            .get(&hive_id)
3727            .ok_or_else(|| CommError::HiveError(format!("Hive {hive_id} not found")))?;
3728        Ok(serde_json::json!({
3729            "hive_id": hive_id,
3730            "hive_name": hive.name,
3731            "question": question,
3732            "timeout_ms": timeout_ms,
3733            "members": hive.constituents.len(),
3734            "status": "thought_broadcast",
3735        }))
3736    }
3737
3738    /// Initiate a deep mind-meld session with a partner agent.
3739    pub fn initiate_meld(
3740        &mut self,
3741        partner_id: &str,
3742        depth: &str,
3743        duration_ms: u64,
3744    ) -> MeldSession {
3745        let id = format!("meld-{}", Utc::now().timestamp_millis());
3746        let session = MeldSession {
3747            id: id.clone(),
3748            partner_id: partner_id.to_string(),
3749            depth: depth.to_string(),
3750            start_time: Utc::now().timestamp() as u64,
3751            duration_ms,
3752            active: true,
3753        };
3754        self.meld_sessions.push(session.clone());
3755        session
3756    }
3757
3758    // -----------------------------------------------------------------------
3759    // Consent flow (pending requests)
3760    // -----------------------------------------------------------------------
3761
3762    /// List pending consent requests.
3763    pub fn list_pending_consent(
3764        &self,
3765        agent_id: Option<&str>,
3766        consent_type: Option<&str>,
3767    ) -> Vec<&ConsentRequest> {
3768        self.pending_consent_requests
3769            .iter()
3770            .filter(|r| {
3771                !r.responded
3772                    && agent_id.map_or(true, |a| r.to == a || r.from == a)
3773                    && consent_type.map_or(true, |ct| r.consent_type == ct)
3774            })
3775            .collect()
3776    }
3777
3778    /// Respond to a pending consent request.
3779    pub fn respond_consent(
3780        &mut self,
3781        request_id: &str,
3782        response: &str,
3783    ) -> CommResult<()> {
3784        let req = self
3785            .pending_consent_requests
3786            .iter_mut()
3787            .find(|r| r.id == request_id)
3788            .ok_or_else(|| {
3789                CommError::ConsentError(format!("Consent request '{request_id}' not found"))
3790            })?;
3791        if req.responded {
3792            return Err(CommError::ConsentError(format!(
3793                "Consent request '{request_id}' already responded"
3794            )));
3795        }
3796        req.responded = true;
3797        req.response = Some(response.to_string());
3798        Ok(())
3799    }
3800
3801    // -----------------------------------------------------------------------
3802    // Query helpers
3803    // -----------------------------------------------------------------------
3804
3805    /// Query relationships for an agent including trust, channels, and consent.
3806    pub fn query_relationships(
3807        &self,
3808        agent_id: &str,
3809        relationship_type: Option<&str>,
3810        depth: u64,
3811    ) -> serde_json::Value {
3812        let _ = depth;
3813        let mut relationships = Vec::new();
3814
3815        // Trust relationships
3816        if relationship_type.is_none() || relationship_type == Some("trust") {
3817            if let Some(level) = self.trust_levels.get(agent_id) {
3818                relationships.push(serde_json::json!({
3819                    "type": "trust",
3820                    "agent": agent_id,
3821                    "level": level.to_string(),
3822                }));
3823            }
3824            // Also find agents that trust this agent
3825            for (other, level) in &self.trust_levels {
3826                if other != agent_id {
3827                    relationships.push(serde_json::json!({
3828                        "type": "trusted_by",
3829                        "agent": other,
3830                        "level": level.to_string(),
3831                    }));
3832                }
3833            }
3834        }
3835
3836        // Channel co-membership
3837        if relationship_type.is_none() || relationship_type == Some("channel") {
3838            for channel in self.channels.values() {
3839                if channel.participants.contains(&agent_id.to_string()) {
3840                    for peer in &channel.participants {
3841                        if peer != agent_id {
3842                            relationships.push(serde_json::json!({
3843                                "type": "channel_peer",
3844                                "agent": peer,
3845                                "channel_id": channel.id,
3846                                "channel_name": channel.name,
3847                            }));
3848                        }
3849                    }
3850                }
3851            }
3852        }
3853
3854        // Consent relationships
3855        if relationship_type.is_none() || relationship_type == Some("consent") {
3856            for gate in &self.consent_gates {
3857                if gate.grantor == agent_id {
3858                    relationships.push(serde_json::json!({
3859                        "type": "consent_granted_to",
3860                        "agent": gate.grantee,
3861                        "scope": format!("{:?}", gate.scope),
3862                        "status": format!("{:?}", gate.status),
3863                    }));
3864                }
3865                if gate.grantee == agent_id {
3866                    relationships.push(serde_json::json!({
3867                        "type": "consent_received_from",
3868                        "agent": gate.grantor,
3869                        "scope": format!("{:?}", gate.scope),
3870                        "status": format!("{:?}", gate.status),
3871                    }));
3872                }
3873            }
3874        }
3875
3876        // Hive co-membership
3877        if relationship_type.is_none() || relationship_type == Some("hive") {
3878            for hive in self.hive_minds.values() {
3879                let is_member = hive.constituents.iter().any(|c| c.agent_id == agent_id);
3880                if is_member {
3881                    for constituent in &hive.constituents {
3882                        if constituent.agent_id != agent_id {
3883                            relationships.push(serde_json::json!({
3884                                "type": "hive_peer",
3885                                "agent": constituent.agent_id,
3886                                "hive_id": hive.id,
3887                                "hive_name": hive.name,
3888                                "role": format!("{:?}", constituent.role),
3889                            }));
3890                        }
3891                    }
3892                }
3893            }
3894        }
3895
3896        serde_json::json!({
3897            "agent_id": agent_id,
3898            "relationship_count": relationships.len(),
3899            "relationships": relationships,
3900        })
3901    }
3902
3903    /// Query the conversation state at a specific point in time.
3904    pub fn conversation_at_time(&self, channel_id: u64, timestamp: u64) -> serde_json::Value {
3905        let messages: Vec<&Message> = self.messages.values()
3906            .filter(|m| m.channel_id == channel_id && m.timestamp.timestamp() as u64 <= timestamp)
3907            .collect();
3908        let channel = self.channels.get(&channel_id);
3909        serde_json::json!({
3910            "channel_id": channel_id,
3911            "channel_name": channel.map(|c| c.name.as_str()).unwrap_or("unknown"),
3912            "as_of": timestamp,
3913            "message_count": messages.len(),
3914            "participants": channel.map(|c| &c.participants),
3915            "last_message": messages.last().map(|m| serde_json::json!({
3916                "id": m.id,
3917                "sender": m.sender,
3918                "content": m.content,
3919                "timestamp": m.timestamp.to_rfc3339(),
3920            })),
3921        })
3922    }
3923
3924    /// Get changes between two timestamps for a channel.
3925    pub fn changes_in_range(&self, channel_id: u64, start: u64, end: u64) -> serde_json::Value {
3926        let messages: Vec<&Message> = self.messages.values()
3927            .filter(|m| {
3928                m.channel_id == channel_id
3929                    && m.timestamp.timestamp() as u64 >= start
3930                    && m.timestamp.timestamp() as u64 <= end
3931            })
3932            .collect();
3933        let new_participants: Vec<String> = Vec::new(); // Would need join/leave events
3934        serde_json::json!({
3935            "channel_id": channel_id,
3936            "start": start,
3937            "end": end,
3938            "messages_added": messages.len(),
3939            "senders": messages.iter().map(|m| m.sender.clone()).collect::<std::collections::HashSet<_>>(),
3940            "message_types": messages.iter().map(|m| format!("{:?}", m.message_type)).collect::<std::collections::HashSet<_>>(),
3941            "new_participants": new_participants,
3942        })
3943    }
3944
3945    /// Query conversation echoes (messages that reference or reply to a message).
3946    pub fn query_echoes(
3947        &self,
3948        message_id: u64,
3949        depth: u64,
3950    ) -> CommResult<serde_json::Value> {
3951        let msg = self
3952            .messages
3953            .get(&message_id)
3954            .ok_or(CommError::MessageNotFound(message_id))?;
3955        let _ = depth;
3956        // Find messages that mention the same topic or are in the same channel
3957        let echoes: Vec<serde_json::Value> = self
3958            .messages
3959            .values()
3960            .filter(|m| m.channel_id == msg.channel_id && m.id != message_id)
3961            .take(50)
3962            .map(|m| {
3963                serde_json::json!({
3964                    "message_id": m.id,
3965                    "sender": m.sender,
3966                    "channel_id": m.channel_id,
3967                    "timestamp": m.timestamp.to_rfc3339(),
3968                })
3969            })
3970            .collect();
3971        Ok(serde_json::json!({
3972            "source_message_id": message_id,
3973            "echo_count": echoes.len(),
3974            "echoes": echoes,
3975        }))
3976    }
3977
3978    /// Query conversation summaries.
3979    pub fn query_conversations(
3980        &self,
3981        channel_id: Option<u64>,
3982        participant: Option<&str>,
3983        limit: u64,
3984    ) -> Vec<ConversationSummary> {
3985        let mut summaries: Vec<ConversationSummary> = self
3986            .channels
3987            .values()
3988            .filter(|ch| {
3989                channel_id.map_or(true, |cid| ch.id == cid)
3990                    && participant.map_or(true, |p| ch.participants.contains(&p.to_string()))
3991            })
3992            .map(|ch| {
3993                let msg_count = self
3994                    .messages
3995                    .values()
3996                    .filter(|m| m.channel_id == ch.id)
3997                    .count() as u64;
3998                let last_activity = self
3999                    .messages
4000                    .values()
4001                    .filter(|m| m.channel_id == ch.id)
4002                    .map(|m| m.timestamp.timestamp() as u64)
4003                    .max()
4004                    .unwrap_or(0);
4005                ConversationSummary {
4006                    channel_id: ch.id,
4007                    participants: ch.participants.clone(),
4008                    message_count: msg_count,
4009                    last_activity,
4010                }
4011            })
4012            .collect();
4013        summaries.truncate(limit as usize);
4014        summaries
4015    }
4016
4017    // -----------------------------------------------------------------------
4018    // Federation extensions
4019    // -----------------------------------------------------------------------
4020
4021    /// Get federation status.
4022    pub fn get_federation_status(&self) -> serde_json::Value {
4023        serde_json::json!({
4024            "enabled": self.federation_config.enabled,
4025            "local_zone": self.federation_config.local_zone,
4026            "zone_count": self.federation_config.zones.len(),
4027            "zones": self.federation_config.zones.iter().map(|z| &z.zone_id).collect::<Vec<_>>(),
4028            "default_policy": format!("{}", self.federation_config.default_policy),
4029        })
4030    }
4031
4032    /// Set federation policy for a zone.
4033    pub fn set_federation_policy(
4034        &mut self,
4035        zone_id: &str,
4036        allow_semantic: bool,
4037        allow_affect: bool,
4038        allow_hive: bool,
4039        max_message_size: u64,
4040    ) -> ZonePolicyConfig {
4041        let config = ZonePolicyConfig {
4042            zone_id: zone_id.to_string(),
4043            allow_semantic,
4044            allow_affect,
4045            allow_hive,
4046            max_message_size,
4047        };
4048        self.zone_policies.insert(zone_id.to_string(), config.clone());
4049        config
4050    }
4051
4052    // -----------------------------------------------------------------------
4053    // Grounding (mirrors memory's memory_ground)
4054    // -----------------------------------------------------------------------
4055
4056    /// Ground a claim against the communication store.
4057    pub fn ground_claim(&self, claim: &str) -> GroundingResult {
4058        let claim_lower = claim.to_lowercase();
4059        let mut evidence = Vec::new();
4060        let mut score = 0.0f64;
4061
4062        // Check channels
4063        for ch in self.channels.values() {
4064            if claim_lower.contains(&ch.name.to_lowercase()) {
4065                evidence.push(GroundingEvidence {
4066                    evidence_type: "channel".to_string(),
4067                    source: String::new(),
4068                    timestamp: 0,
4069                    content: format!("Channel '{}' (id={}, state={})", ch.name, ch.id, ch.state),
4070                    relevance: 0.9,
4071                });
4072                score = score.max(0.8);
4073            }
4074            for p in &ch.participants {
4075                if claim_lower.contains(&p.to_lowercase()) {
4076                    evidence.push(GroundingEvidence {
4077                        evidence_type: "participant".to_string(),
4078                        source: String::new(),
4079                        timestamp: 0,
4080                        content: format!("'{}' is a participant in channel '{}'", p, ch.name),
4081                        relevance: 0.8,
4082                    });
4083                    score = score.max(0.7);
4084                }
4085            }
4086        }
4087
4088        // Check messages
4089        for msg in self.messages.values() {
4090            if claim_lower.contains(&msg.sender.to_lowercase())
4091                || claim_lower.contains(&msg.content.to_lowercase().chars().take(50).collect::<String>())
4092            {
4093                evidence.push(GroundingEvidence {
4094                    evidence_type: "message".to_string(),
4095                    source: String::new(),
4096                    timestamp: 0,
4097                    content: format!(
4098                        "Message from '{}' in channel {} at {}",
4099                        msg.sender, msg.channel_id, msg.timestamp
4100                    ),
4101                    relevance: 0.7,
4102                });
4103                score = score.max(0.6);
4104            }
4105        }
4106
4107        // Check consent gates
4108        for gate in &self.consent_gates {
4109            if claim_lower.contains(&gate.grantor.to_lowercase())
4110                || claim_lower.contains(&gate.grantee.to_lowercase())
4111            {
4112                evidence.push(GroundingEvidence {
4113                    evidence_type: "consent".to_string(),
4114                    source: String::new(),
4115                    timestamp: 0,
4116                    content: format!(
4117                        "Consent: {} -> {} ({}, status={})",
4118                        gate.grantor, gate.grantee, gate.scope, gate.status
4119                    ),
4120                    relevance: 0.8,
4121                });
4122                score = score.max(0.7);
4123            }
4124        }
4125
4126        // Check trust levels
4127        for (agent, level) in &self.trust_levels {
4128            if claim_lower.contains(&agent.to_lowercase()) {
4129                evidence.push(GroundingEvidence {
4130                    evidence_type: "trust".to_string(),
4131                    source: String::new(),
4132                    timestamp: 0,
4133                    content: format!("Trust level for '{}': {}", agent, level),
4134                    relevance: 0.8,
4135                });
4136                score = score.max(0.7);
4137            }
4138        }
4139
4140        // Check hive minds
4141        for hive in self.hive_minds.values() {
4142            if claim_lower.contains(&hive.name.to_lowercase()) {
4143                evidence.push(GroundingEvidence {
4144                    evidence_type: "hive".to_string(),
4145                    source: String::new(),
4146                    timestamp: 0,
4147                    content: format!(
4148                        "Hive '{}' (id={}, members={})",
4149                        hive.name, hive.id, hive.constituents.len()
4150                    ),
4151                    relevance: 0.8,
4152                });
4153                score = score.max(0.7);
4154            }
4155        }
4156
4157        let status = if score >= 0.7 {
4158            GroundingStatus::Verified
4159        } else if score >= 0.3 {
4160            GroundingStatus::Partial
4161        } else {
4162            GroundingStatus::Ungrounded
4163        };
4164
4165        GroundingResult {
4166            claim: claim.to_string(),
4167            status,
4168            evidence,
4169            confidence: score,
4170        }
4171    }
4172
4173    // -----------------------------------------------------------------------
4174    // Key management
4175    // -----------------------------------------------------------------------
4176
4177    /// Generate a new key entry with metadata.
4178    ///
4179    /// Creates a key entry with a pseudo-random fingerprint. This is a stub
4180    /// that manages key metadata; real cryptographic key material would be
4181    /// generated by a dedicated crypto layer.
4182    pub fn generate_key(
4183        &mut self,
4184        algorithm: &str,
4185        channel_id: Option<u64>,
4186    ) -> CommResult<KeyEntry> {
4187        let id = self.next_key_id;
4188        self.next_key_id += 1;
4189
4190        // Generate a pseudo-random fingerprint from id + timestamp
4191        let now = std::time::SystemTime::now()
4192            .duration_since(std::time::UNIX_EPOCH)
4193            .unwrap_or_default()
4194            .as_secs();
4195        let fingerprint = format!("{:016x}", now.wrapping_mul(6364136223846793005).wrapping_add(id));
4196
4197        let entry = KeyEntry {
4198            id,
4199            algorithm: algorithm.to_string(),
4200            created_at: now,
4201            status: "active".to_string(),
4202            channel_id,
4203            fingerprint,
4204        };
4205
4206        self.key_store.push(entry.clone());
4207        Ok(entry)
4208    }
4209
4210    /// List all key entries.
4211    pub fn list_keys(&self) -> Vec<&KeyEntry> {
4212        self.key_store.iter().collect()
4213    }
4214
4215    /// Get a specific key by ID.
4216    pub fn get_key(&self, key_id: u64) -> CommResult<&KeyEntry> {
4217        self.key_store
4218            .iter()
4219            .find(|k| k.id == key_id)
4220            .ok_or(CommError::KeyNotFound(key_id))
4221    }
4222
4223    /// Rotate a key: mark the old key as "rotated" and create a new key
4224    /// with the same algorithm and channel binding.
4225    pub fn rotate_key(&mut self, key_id: u64) -> CommResult<KeyEntry> {
4226        // Find and mark old key as rotated
4227        let (algorithm, channel_id) = {
4228            let old_key = self
4229                .key_store
4230                .iter_mut()
4231                .find(|k| k.id == key_id)
4232                .ok_or(CommError::KeyNotFound(key_id))?;
4233
4234            if old_key.status == "revoked" {
4235                return Err(CommError::KeyNotFound(key_id));
4236            }
4237
4238            let alg = old_key.algorithm.clone();
4239            let ch = old_key.channel_id;
4240            old_key.status = "rotated".to_string();
4241            (alg, ch)
4242        };
4243
4244        // Generate a new key with the same settings
4245        self.generate_key(&algorithm, channel_id)
4246    }
4247
4248    /// Revoke a key by ID.
4249    pub fn revoke_key(&mut self, key_id: u64) -> CommResult<()> {
4250        let key = self
4251            .key_store
4252            .iter_mut()
4253            .find(|k| k.id == key_id)
4254            .ok_or(CommError::KeyNotFound(key_id))?;
4255
4256        key.status = "revoked".to_string();
4257        Ok(())
4258    }
4259
4260    /// Export a key's fingerprint (stub for real key export).
4261    pub fn export_key(&self, key_id: u64) -> CommResult<String> {
4262        let key = self.get_key(key_id)?;
4263        Ok(key.fingerprint.clone())
4264    }
4265
4266    // -----------------------------------------------------------------------
4267    // Grounding: evidence search & fuzzy suggest
4268    // -----------------------------------------------------------------------
4269
4270    /// Search messages, channels, and agents for evidence matching a query.
4271    ///
4272    /// Returns detailed evidence entries with timestamps and relevance scores.
4273    pub fn ground_evidence(&self, query: &str) -> Vec<GroundingEvidence> {
4274        let query_lower = query.to_lowercase();
4275        let mut evidence = Vec::new();
4276
4277        // Search messages
4278        for msg in self.messages.values() {
4279            let content_lower = msg.content.to_lowercase();
4280            let sender_lower = msg.sender.to_lowercase();
4281            if content_lower.contains(&query_lower) || sender_lower.contains(&query_lower) {
4282                let relevance = if content_lower.contains(&query_lower) && sender_lower.contains(&query_lower) {
4283                    1.0
4284                } else if content_lower.contains(&query_lower) {
4285                    0.8
4286                } else {
4287                    0.6
4288                };
4289                evidence.push(GroundingEvidence {
4290                    evidence_type: format!("message(id={}, ts={})", msg.id, msg.timestamp.timestamp()),
4291                    source: "messages".to_string(),
4292                    timestamp: msg.timestamp.timestamp() as u64,
4293                    content: format!(
4294                        "[{}] {}: {}",
4295                        msg.timestamp.to_rfc3339(),
4296                        msg.sender,
4297                        msg.content.chars().take(200).collect::<String>()
4298                    ),
4299                    relevance,
4300                });
4301            }
4302        }
4303
4304        // Search channels
4305        for ch in self.channels.values() {
4306            if ch.name.to_lowercase().contains(&query_lower) {
4307                evidence.push(GroundingEvidence {
4308                    evidence_type: format!("channel(id={}, created={})", ch.id, ch.created_at.timestamp()),
4309                    source: "channels".to_string(),
4310                    timestamp: ch.created_at.timestamp() as u64,
4311                    content: format!(
4312                        "Channel '{}' (type={}, state={}, participants={})",
4313                        ch.name, ch.channel_type, ch.state, ch.participants.len()
4314                    ),
4315                    relevance: 0.9,
4316                });
4317            }
4318            // Search participants
4319            for p in &ch.participants {
4320                if p.to_lowercase().contains(&query_lower) {
4321                    evidence.push(GroundingEvidence {
4322                        evidence_type: format!("agent(channel={})", ch.name),
4323                        source: "agents".to_string(),
4324                        timestamp: 0,
4325                        content: format!("Agent '{}' in channel '{}'", p, ch.name),
4326                        relevance: 0.7,
4327                    });
4328                }
4329            }
4330        }
4331
4332        // Search hive minds
4333        for hive in self.hive_minds.values() {
4334            if hive.name.to_lowercase().contains(&query_lower) {
4335                evidence.push(GroundingEvidence {
4336                    evidence_type: format!("hive(id={})", hive.id),
4337                    source: "hives".to_string(),
4338                    timestamp: 0,
4339                    content: format!(
4340                        "Hive '{}' with {} constituents",
4341                        hive.name,
4342                        hive.constituents.len()
4343                    ),
4344                    relevance: 0.8,
4345                });
4346            }
4347        }
4348
4349        // Sort by relevance descending
4350        evidence.sort_by(|a, b| b.relevance.partial_cmp(&a.relevance).unwrap_or(std::cmp::Ordering::Equal));
4351        evidence
4352    }
4353
4354    /// Return fuzzy/contains suggestions based on agent names, channel names,
4355    /// or message content matching the query.
4356    pub fn ground_suggest(&self, query: &str, limit: usize) -> Vec<String> {
4357        let query_lower = query.to_lowercase();
4358        let mut suggestions = Vec::new();
4359
4360        // Suggest channel names
4361        for ch in self.channels.values() {
4362            if ch.name.to_lowercase().contains(&query_lower) {
4363                suggestions.push(format!("channel:{}", ch.name));
4364            }
4365        }
4366
4367        // Suggest agent names from participants
4368        let mut seen_agents = std::collections::HashSet::new();
4369        for ch in self.channels.values() {
4370            for p in &ch.participants {
4371                if p.to_lowercase().contains(&query_lower) && seen_agents.insert(p.clone()) {
4372                    suggestions.push(format!("agent:{}", p));
4373                }
4374            }
4375        }
4376
4377        // Suggest from trust levels
4378        for agent in self.trust_levels.keys() {
4379            if agent.to_lowercase().contains(&query_lower) && seen_agents.insert(agent.clone()) {
4380                suggestions.push(format!("agent:{}", agent));
4381            }
4382        }
4383
4384        // Suggest hive mind names
4385        for hive in self.hive_minds.values() {
4386            if hive.name.to_lowercase().contains(&query_lower) {
4387                suggestions.push(format!("hive:{}", hive.name));
4388            }
4389        }
4390
4391        // Suggest from message content (unique snippets)
4392        let mut content_seen = std::collections::HashSet::new();
4393        for msg in self.messages.values() {
4394            if msg.content.to_lowercase().contains(&query_lower) {
4395                let snippet: String = msg.content.chars().take(80).collect();
4396                if content_seen.insert(snippet.clone()) {
4397                    suggestions.push(format!("message:{}", snippet));
4398                }
4399            }
4400        }
4401
4402        suggestions.truncate(limit);
4403        suggestions
4404    }
4405
4406    // -----------------------------------------------------------------------
4407    // CommId and MessageContent helpers
4408    // -----------------------------------------------------------------------
4409
4410    /// Assign CommIds to all messages and channels that don't already have one.
4411    ///
4412    /// Deterministically derives the UUID from the legacy u64 id so that
4413    /// repeated calls are idempotent.
4414    pub fn assign_comm_ids(&mut self) {
4415        let msg_ids: Vec<u64> = self.messages.keys().copied().collect();
4416        for id in msg_ids {
4417            if let Some(msg) = self.messages.get_mut(&id) {
4418                if msg.comm_id.is_none() {
4419                    msg.comm_id = Some(CommId::from_u64(msg.id));
4420                }
4421            }
4422        }
4423        let chan_ids: Vec<u64> = self.channels.keys().copied().collect();
4424        for id in chan_ids {
4425            if let Some(channel) = self.channels.get_mut(&id) {
4426                if channel.comm_id.is_none() {
4427                    channel.comm_id = Some(CommId::from_u64(channel.id));
4428                }
4429            }
4430        }
4431    }
4432
4433    /// Look up a message by its CommId.
4434    pub fn get_message_by_comm_id(&self, comm_id: &CommId) -> Option<&Message> {
4435        self.messages.values().find(|m| m.comm_id.as_ref() == Some(comm_id))
4436    }
4437
4438    /// Look up a channel by its CommId.
4439    pub fn get_channel_by_comm_id(&self, comm_id: &CommId) -> Option<&Channel> {
4440        self.channels.values().find(|c| c.comm_id.as_ref() == Some(comm_id))
4441    }
4442
4443    /// Send a message with rich content.
4444    ///
4445    /// Sends a regular message and attaches a `MessageContent` (serialized
4446    /// as JSON) to the `rich_content_json` field.
4447    pub fn send_rich_message(
4448        &mut self,
4449        channel_id: u64,
4450        sender: &str,
4451        content: MessageContent,
4452        msg_type: MessageType,
4453    ) -> CommResult<Message> {
4454        let text = content.as_text().to_string();
4455        let rich_json = serde_json::to_string(&content)
4456            .map_err(|e| CommError::InvalidContent(format!("Failed to serialize rich content: {}", e)))?;
4457        let mut msg = self.send_message(channel_id, sender, &text, msg_type)?;
4458        // Update the stored message with rich content
4459        if let Some(stored) = self.messages.get_mut(&msg.id) {
4460            stored.rich_content_json = Some(rich_json.clone());
4461            msg.rich_content_json = Some(rich_json);
4462        }
4463        Ok(msg)
4464    }
4465
4466    /// Get the rich content of a message (if any).
4467    pub fn get_rich_content(&self, message_id: u64) -> CommResult<Option<MessageContent>> {
4468        let msg = self.messages.get(&message_id)
4469            .ok_or(CommError::MessageNotFound(message_id))?;
4470        match &msg.rich_content_json {
4471            Some(json_str) => {
4472                let content: MessageContent = serde_json::from_str(json_str)
4473                    .map_err(|e| CommError::InvalidContent(format!("Failed to parse rich content: {}", e)))?;
4474                Ok(Some(content))
4475            }
4476            None => Ok(None),
4477        }
4478    }
4479
4480    // -----------------------------------------------------------------------
4481    // Semantic vector search
4482    // -----------------------------------------------------------------------
4483
4484    /// Store an embedding vector for a message.
4485    ///
4486    /// The embedding is typically produced by an external model (e.g. an LLM
4487    /// embedding endpoint) and associated with the message's ID so that
4488    /// [`semantic_search`](Self::semantic_search) can find semantically
4489    /// similar messages later.
4490    pub fn store_embedding(&mut self, message_id: u64, embedding: Vec<f32>) {
4491        self.embeddings.insert(message_id, embedding);
4492    }
4493
4494    /// Find the top-k most semantically similar messages to a query embedding.
4495    ///
4496    /// Performs brute-force cosine similarity over all stored embeddings and
4497    /// returns up to `top_k` results sorted by descending similarity.  Each
4498    /// result is a `(message_id, similarity)` pair where similarity is in
4499    /// the range `[-1.0, 1.0]`.
4500    pub fn semantic_search(
4501        &self,
4502        query_embedding: &[f32],
4503        top_k: usize,
4504    ) -> Vec<(u64, f32)> {
4505        let mut scored: Vec<(u64, f32)> = self
4506            .embeddings
4507            .iter()
4508            .map(|(&msg_id, emb)| (msg_id, Self::cosine_similarity(query_embedding, emb)))
4509            .collect();
4510
4511        // Sort descending by similarity.
4512        scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
4513        scored.truncate(top_k);
4514        scored
4515    }
4516
4517    /// Compute the cosine similarity between two vectors.
4518    ///
4519    /// Returns a value in `[-1.0, 1.0]`.  If either vector has zero
4520    /// magnitude the function returns `0.0` to avoid division by zero.
4521    pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
4522        let len = a.len().min(b.len());
4523        let mut dot = 0.0_f32;
4524        let mut norm_a = 0.0_f32;
4525        let mut norm_b = 0.0_f32;
4526
4527        for i in 0..len {
4528            dot += a[i] * b[i];
4529            norm_a += a[i] * a[i];
4530            norm_b += b[i] * b[i];
4531        }
4532
4533        let denom = norm_a.sqrt() * norm_b.sqrt();
4534        if denom == 0.0 {
4535            0.0
4536        } else {
4537            dot / denom
4538        }
4539    }
4540}
4541
4542/// Summary statistics for a CommStore.
4543#[derive(Debug, Clone, Serialize, Deserialize)]
4544pub struct CommStoreStats {
4545    /// Number of channels.
4546    pub channel_count: usize,
4547    /// Number of messages.
4548    pub message_count: usize,
4549    /// Number of active subscriptions.
4550    pub subscription_count: usize,
4551    /// Total number of participants across all channels.
4552    pub total_participants: usize,
4553    /// Number of messages in the dead letter queue.
4554    #[serde(default)]
4555    pub dead_letter_count: usize,
4556    /// Count of messages grouped by message type.
4557    #[serde(default)]
4558    pub messages_by_type: HashMap<String, usize>,
4559    /// Count of messages grouped by priority.
4560    #[serde(default)]
4561    pub messages_by_priority: HashMap<String, usize>,
4562    /// Count of channels grouped by state.
4563    #[serde(default)]
4564    pub channels_by_state: HashMap<String, usize>,
4565    /// Timestamp of the oldest message in the store.
4566    #[serde(default)]
4567    pub oldest_message: Option<DateTime<Utc>>,
4568    /// Timestamp of the newest message in the store.
4569    #[serde(default)]
4570    pub newest_message: Option<DateTime<Utc>>,
4571    /// Number of consent gates.
4572    #[serde(default)]
4573    pub consent_gate_count: usize,
4574    /// Number of trust level overrides.
4575    #[serde(default)]
4576    pub trust_override_count: usize,
4577    /// Number of scheduled temporal messages.
4578    #[serde(default)]
4579    pub temporal_queue_count: usize,
4580    /// Number of hive minds.
4581    #[serde(default)]
4582    pub hive_count: usize,
4583    /// Number of communication log entries.
4584    #[serde(default)]
4585    pub comm_log_count: usize,
4586    /// Whether federation is enabled.
4587    #[serde(default)]
4588    pub federation_enabled: bool,
4589    /// Number of federated zones.
4590    #[serde(default)]
4591    pub federated_zone_count: usize,
4592    /// Number of audit log entries.
4593    #[serde(default)]
4594    pub audit_log_count: usize,
4595}
4596
4597// ---------------------------------------------------------------------------
4598// MessageEngine — unified message processing pipeline
4599// ---------------------------------------------------------------------------
4600
4601/// Unified message processing engine that wraps [`CommStore`] and provides
4602/// a higher-level pipeline for sending and querying messages.
4603///
4604/// The engine adds validation, trust checking, and consent enforcement as
4605/// a composable layer on top of the raw store operations.
4606#[derive(Debug, Clone)]
4607pub struct MessageEngine {
4608    /// The underlying communication store.
4609    pub store: CommStore,
4610}
4611
4612impl Default for MessageEngine {
4613    fn default() -> Self {
4614        Self::new()
4615    }
4616}
4617
4618impl MessageEngine {
4619    /// Create a new engine backed by an empty [`CommStore`].
4620    pub fn new() -> Self {
4621        Self {
4622            store: CommStore::new(),
4623        }
4624    }
4625
4626    /// Create an engine from an existing [`CommStore`].
4627    pub fn from_store(store: CommStore) -> Self {
4628        Self { store }
4629    }
4630
4631    /// Process an inbound message through the full pipeline.
4632    ///
4633    /// Steps:
4634    /// 1. Parse the message type from a string.
4635    /// 2. Validate sender trust level (must be at least `Basic`).
4636    /// 3. Check consent for the SendMessages scope.
4637    /// 4. Send the message via the underlying store.
4638    /// 5. Return the new message ID.
4639    pub fn process_message(
4640        &mut self,
4641        channel_id: u64,
4642        sender: &str,
4643        content: &str,
4644        message_type: &str,
4645    ) -> Result<u64, String> {
4646        // 1. Parse message type
4647        let msg_type: MessageType = message_type
4648            .parse()
4649            .map_err(|e: String| format!("Invalid message type: {e}"))?;
4650
4651        // 2. Validate sender trust — require at least Basic
4652        let trust = self.store.get_trust_level(sender);
4653        if trust < CommTrustLevel::Basic {
4654            return Err(format!(
4655                "Sender '{}' has insufficient trust level: {} (need at least basic)",
4656                sender, trust
4657            ));
4658        }
4659
4660        // 3. Check consent (SendMessages scope) — open-by-default when no
4661        //    gates are configured for the scope.
4662        if !self.store.check_consent_for_action(sender, "", ConsentScope::SendMessages) {
4663            return Err(format!(
4664                "Consent denied: sender '{}' lacks SendMessages consent",
4665                sender
4666            ));
4667        }
4668
4669        // 4. Send via store
4670        let message = self
4671            .store
4672            .send_message(channel_id, sender, content, msg_type)
4673            .map_err(|e| e.to_string())?;
4674
4675        // 5. Return message ID
4676        Ok(message.id)
4677    }
4678
4679    /// Unified query dispatcher.
4680    ///
4681    /// Supported `query_type` values:
4682    ///
4683    /// - `"channel_messages"` — returns messages for a channel.
4684    ///   Params: `{ "channel_id": <u64> }`
4685    ///
4686    /// - `"message"` — returns a single message by ID.
4687    ///   Params: `{ "message_id": <u64> }`
4688    ///
4689    /// - `"stats"` — returns store-level statistics.
4690    ///   Params: `{}` (none required)
4691    ///
4692    /// - `"semantic_search"` — returns semantically similar messages.
4693    ///   Params: `{ "embedding": [f32, ...], "top_k": <usize> }`
4694    pub fn query(
4695        &self,
4696        query_type: &str,
4697        params: &serde_json::Value,
4698    ) -> Result<serde_json::Value, String> {
4699        match query_type {
4700            "channel_messages" => {
4701                let channel_id = params
4702                    .get("channel_id")
4703                    .and_then(|v| v.as_u64())
4704                    .ok_or_else(|| "Missing or invalid 'channel_id' parameter".to_string())?;
4705
4706                let msgs: Vec<&Message> = self
4707                    .store
4708                    .messages
4709                    .values()
4710                    .filter(|m| m.channel_id == channel_id)
4711                    .collect();
4712
4713                serde_json::to_value(&msgs).map_err(|e| e.to_string())
4714            }
4715
4716            "message" => {
4717                let message_id = params
4718                    .get("message_id")
4719                    .and_then(|v| v.as_u64())
4720                    .ok_or_else(|| "Missing or invalid 'message_id' parameter".to_string())?;
4721
4722                let msg = self
4723                    .store
4724                    .messages
4725                    .get(&message_id)
4726                    .ok_or_else(|| format!("Message {} not found", message_id))?;
4727
4728                serde_json::to_value(msg).map_err(|e| e.to_string())
4729            }
4730
4731            "stats" => {
4732                let stats = self.store.stats();
4733                serde_json::to_value(&stats).map_err(|e| e.to_string())
4734            }
4735
4736            "semantic_search" => {
4737                let embedding_val = params
4738                    .get("embedding")
4739                    .and_then(|v| v.as_array())
4740                    .ok_or_else(|| "Missing or invalid 'embedding' parameter".to_string())?;
4741
4742                let embedding: Vec<f32> = embedding_val
4743                    .iter()
4744                    .filter_map(|v| v.as_f64().map(|f| f as f32))
4745                    .collect();
4746
4747                let top_k = params
4748                    .get("top_k")
4749                    .and_then(|v| v.as_u64())
4750                    .unwrap_or(10) as usize;
4751
4752                let results = self.store.semantic_search(&embedding, top_k);
4753                serde_json::to_value(&results).map_err(|e| e.to_string())
4754            }
4755
4756            other => Err(format!("Unknown query type: '{}'", other)),
4757        }
4758    }
4759}
4760
4761// ---------------------------------------------------------------------------
4762// Tests
4763// ---------------------------------------------------------------------------
4764
4765#[cfg(test)]
4766mod tests {
4767    use super::*;
4768    use filetime::FileTime;
4769
4770    fn new_store_with_channel() -> (CommStore, u64) {
4771        let mut store = CommStore::new();
4772        let ch = store
4773            .create_channel("test-channel", ChannelType::Group, None)
4774            .unwrap();
4775        (store, ch.id)
4776    }
4777
4778    // -- Channel tests --
4779
4780    #[test]
4781    fn test_create_channel() {
4782        let mut store = CommStore::new();
4783        let ch = store
4784            .create_channel("my-channel", ChannelType::Group, None)
4785            .unwrap();
4786        assert_eq!(ch.name, "my-channel");
4787        assert_eq!(ch.channel_type, ChannelType::Group);
4788        assert!(ch.participants.is_empty());
4789    }
4790
4791    #[test]
4792    fn test_create_channel_invalid_name_empty() {
4793        let mut store = CommStore::new();
4794        let result = store.create_channel("", ChannelType::Group, None);
4795        assert!(result.is_err());
4796    }
4797
4798    #[test]
4799    fn test_create_channel_invalid_name_special_chars() {
4800        let mut store = CommStore::new();
4801        let result = store.create_channel("bad channel!", ChannelType::Group, None);
4802        assert!(result.is_err());
4803    }
4804
4805    #[test]
4806    fn test_create_channel_long_name() {
4807        let mut store = CommStore::new();
4808        let long_name = "a".repeat(129);
4809        let result = store.create_channel(&long_name, ChannelType::Group, None);
4810        assert!(result.is_err());
4811    }
4812
4813    #[test]
4814    fn test_list_channels() {
4815        let mut store = CommStore::new();
4816        store
4817            .create_channel("alpha", ChannelType::Direct, None)
4818            .unwrap();
4819        store
4820            .create_channel("beta", ChannelType::Group, None)
4821            .unwrap();
4822        let channels = store.list_channels();
4823        assert_eq!(channels.len(), 2);
4824        assert_eq!(channels[0].name, "alpha");
4825        assert_eq!(channels[1].name, "beta");
4826    }
4827
4828    #[test]
4829    fn test_get_channel() {
4830        let mut store = CommStore::new();
4831        let ch = store
4832            .create_channel("find-me", ChannelType::Broadcast, None)
4833            .unwrap();
4834        assert!(store.get_channel(ch.id).is_some());
4835        assert!(store.get_channel(999).is_none());
4836    }
4837
4838    #[test]
4839    fn test_join_channel() {
4840        let (mut store, cid) = new_store_with_channel();
4841        store.join_channel(cid, "alice").unwrap();
4842        let ch = store.get_channel(cid).unwrap();
4843        assert_eq!(ch.participants, vec!["alice"]);
4844    }
4845
4846    #[test]
4847    fn test_join_channel_duplicate() {
4848        let (mut store, cid) = new_store_with_channel();
4849        store.join_channel(cid, "alice").unwrap();
4850        let result = store.join_channel(cid, "alice");
4851        assert!(result.is_err());
4852    }
4853
4854    #[test]
4855    fn test_join_channel_full() {
4856        let mut store = CommStore::new();
4857        let config = ChannelConfig {
4858            max_participants: 1,
4859            ..Default::default()
4860        };
4861        let ch = store
4862            .create_channel("tiny", ChannelType::Group, Some(config))
4863            .unwrap();
4864        store.join_channel(ch.id, "alice").unwrap();
4865        let result = store.join_channel(ch.id, "bob");
4866        assert!(result.is_err());
4867    }
4868
4869    #[test]
4870    fn test_leave_channel() {
4871        let (mut store, cid) = new_store_with_channel();
4872        store.join_channel(cid, "alice").unwrap();
4873        store.leave_channel(cid, "alice").unwrap();
4874        let ch = store.get_channel(cid).unwrap();
4875        assert!(ch.participants.is_empty());
4876    }
4877
4878    #[test]
4879    fn test_leave_channel_not_member() {
4880        let (mut store, cid) = new_store_with_channel();
4881        let result = store.leave_channel(cid, "ghost");
4882        assert!(result.is_err());
4883    }
4884
4885    // -- Message tests --
4886
4887    #[test]
4888    fn test_send_message() {
4889        let (mut store, cid) = new_store_with_channel();
4890        let msg = store
4891            .send_message(cid, "alice", "hello world", MessageType::Text)
4892            .unwrap();
4893        assert_eq!(msg.sender, "alice");
4894        assert_eq!(msg.content, "hello world");
4895        assert_eq!(msg.message_type, MessageType::Text);
4896        assert!(msg.signature.is_some());
4897    }
4898
4899    #[test]
4900    fn test_send_message_empty_content() {
4901        let (mut store, cid) = new_store_with_channel();
4902        let result = store.send_message(cid, "alice", "", MessageType::Text);
4903        assert!(result.is_err());
4904    }
4905
4906    #[test]
4907    fn test_send_message_empty_sender() {
4908        let (mut store, cid) = new_store_with_channel();
4909        let result = store.send_message(cid, "", "hi", MessageType::Text);
4910        assert!(result.is_err());
4911    }
4912
4913    #[test]
4914    fn test_send_message_nonexistent_channel() {
4915        let mut store = CommStore::new();
4916        let result = store.send_message(999, "alice", "hi", MessageType::Text);
4917        assert!(result.is_err());
4918    }
4919
4920    #[test]
4921    fn test_receive_messages() {
4922        let (mut store, cid) = new_store_with_channel();
4923        store
4924            .send_message(cid, "alice", "msg1", MessageType::Text)
4925            .unwrap();
4926        store
4927            .send_message(cid, "bob", "msg2", MessageType::Text)
4928            .unwrap();
4929        let msgs = store.receive_messages(cid, None, None).unwrap();
4930        assert_eq!(msgs.len(), 2);
4931    }
4932
4933    #[test]
4934    fn test_receive_messages_with_since() {
4935        let (mut store, cid) = new_store_with_channel();
4936        store
4937            .send_message(cid, "alice", "old msg", MessageType::Text)
4938            .unwrap();
4939        let cutoff = Utc::now();
4940        store
4941            .send_message(cid, "alice", "new msg", MessageType::Text)
4942            .unwrap();
4943        let msgs = store.receive_messages(cid, None, Some(cutoff)).unwrap();
4944        assert_eq!(msgs.len(), 1);
4945        assert_eq!(msgs[0].content, "new msg");
4946    }
4947
4948    #[test]
4949    fn test_acknowledge_message() {
4950        let (mut store, cid) = new_store_with_channel();
4951        let msg = store
4952            .send_message(cid, "alice", "ack me", MessageType::Text)
4953            .unwrap();
4954        store.acknowledge_message(msg.id, "bob").unwrap();
4955        let updated = store.get_message(msg.id).unwrap();
4956        assert!(updated.acknowledged_by.contains(&"bob".to_string()));
4957    }
4958
4959    #[test]
4960    fn test_acknowledge_nonexistent() {
4961        let mut store = CommStore::new();
4962        let result = store.acknowledge_message(999, "bob");
4963        assert!(result.is_err());
4964    }
4965
4966    #[test]
4967    fn test_broadcast() {
4968        let (mut store, cid) = new_store_with_channel();
4969        store.join_channel(cid, "alice").unwrap();
4970        store.join_channel(cid, "bob").unwrap();
4971        store.join_channel(cid, "carol").unwrap();
4972        let msgs = store.broadcast(cid, "alice", "hello everyone").unwrap();
4973        // alice broadcasts to bob and carol (not self)
4974        assert_eq!(msgs.len(), 2);
4975    }
4976
4977    // -- Pub/Sub tests --
4978
4979    #[test]
4980    fn test_subscribe() {
4981        let mut store = CommStore::new();
4982        let sub = store.subscribe("weather", "sensor-1").unwrap();
4983        assert_eq!(sub.topic, "weather");
4984        assert_eq!(sub.subscriber, "sensor-1");
4985    }
4986
4987    #[test]
4988    fn test_unsubscribe() {
4989        let mut store = CommStore::new();
4990        let sub = store.subscribe("weather", "sensor-1").unwrap();
4991        store.unsubscribe(sub.id).unwrap();
4992        assert!(store.unsubscribe(sub.id).is_err());
4993    }
4994
4995    #[test]
4996    fn test_publish() {
4997        let mut store = CommStore::new();
4998        store.subscribe("alerts", "agent-a").unwrap();
4999        store.subscribe("alerts", "agent-b").unwrap();
5000        let msgs = store.publish("alerts", "monitor", "CPU high").unwrap();
5001        assert_eq!(msgs.len(), 2);
5002    }
5003
5004    // -- Query tests --
5005
5006    #[test]
5007    fn test_search_messages() {
5008        let (mut store, cid) = new_store_with_channel();
5009        store
5010            .send_message(cid, "alice", "hello world", MessageType::Text)
5011            .unwrap();
5012        store
5013            .send_message(cid, "bob", "goodbye world", MessageType::Text)
5014            .unwrap();
5015        store
5016            .send_message(cid, "carol", "hello there", MessageType::Text)
5017            .unwrap();
5018        let results = store.search_messages("hello", 10);
5019        assert_eq!(results.len(), 2);
5020    }
5021
5022    #[test]
5023    fn test_query_history_with_filter() {
5024        let (mut store, cid) = new_store_with_channel();
5025        store
5026            .send_message(cid, "alice", "text msg", MessageType::Text)
5027            .unwrap();
5028        store
5029            .send_message(cid, "bob", "command msg", MessageType::Command)
5030            .unwrap();
5031        let filter = MessageFilter {
5032            message_type: Some(MessageType::Command),
5033            ..Default::default()
5034        };
5035        let results = store.query_history(cid, &filter);
5036        assert_eq!(results.len(), 1);
5037        assert_eq!(results[0].message_type, MessageType::Command);
5038    }
5039
5040    #[test]
5041    fn test_get_message() {
5042        let (mut store, cid) = new_store_with_channel();
5043        let msg = store
5044            .send_message(cid, "alice", "find me", MessageType::Text)
5045            .unwrap();
5046        assert!(store.get_message(msg.id).is_some());
5047        assert!(store.get_message(999).is_none());
5048    }
5049
5050    // -- Persistence tests --
5051
5052    #[test]
5053    fn test_save_and_load() {
5054        let (mut store, cid) = new_store_with_channel();
5055        store.join_channel(cid, "alice").unwrap();
5056        store
5057            .send_message(cid, "alice", "persisted", MessageType::Text)
5058            .unwrap();
5059        store.subscribe("topic-a", "alice").unwrap();
5060
5061        let dir = tempfile::tempdir().unwrap();
5062        let path = dir.path().join("test.acomm");
5063        store.save(&path).unwrap();
5064
5065        let loaded = CommStore::load(&path).unwrap();
5066        assert_eq!(loaded.channels.len(), 1);
5067        assert_eq!(loaded.messages.len(), 1);
5068        assert_eq!(loaded.subscriptions.len(), 1);
5069    }
5070
5071    #[test]
5072    fn test_load_invalid_file() {
5073        let dir = tempfile::tempdir().unwrap();
5074        let path = dir.path().join("bad.acomm");
5075        std::fs::write(&path, b"not a valid file").unwrap();
5076        let result = CommStore::load(&path);
5077        assert!(result.is_err());
5078    }
5079
5080    // -- Stats --
5081
5082    #[test]
5083    fn test_stats() {
5084        let (mut store, cid) = new_store_with_channel();
5085        store.join_channel(cid, "alice").unwrap();
5086        store
5087            .send_message(cid, "alice", "hi", MessageType::Text)
5088            .unwrap();
5089        let stats = store.stats();
5090        assert_eq!(stats.channel_count, 1);
5091        assert_eq!(stats.message_count, 1);
5092        assert_eq!(stats.total_participants, 1);
5093    }
5094
5095    // -- Set channel config --
5096
5097    #[test]
5098    fn test_set_channel_config() {
5099        let (mut store, cid) = new_store_with_channel();
5100        let config = ChannelConfig {
5101            max_participants: 10,
5102            ttl_seconds: 3600,
5103            persistence: false,
5104            encryption_required: true,
5105            ..Default::default()
5106        };
5107        store.set_channel_config(cid, config).unwrap();
5108        let ch = store.get_channel(cid).unwrap();
5109        assert_eq!(ch.config.max_participants, 10);
5110        assert!(ch.config.encryption_required);
5111    }
5112
5113    // -- Message type parsing --
5114
5115    #[test]
5116    fn test_message_type_roundtrip() {
5117        let types = vec![
5118            MessageType::Text,
5119            MessageType::Command,
5120            MessageType::Query,
5121            MessageType::Response,
5122            MessageType::Broadcast,
5123            MessageType::Notification,
5124            MessageType::Acknowledgment,
5125            MessageType::Error,
5126        ];
5127        for mt in types {
5128            let s = mt.to_string();
5129            let parsed: MessageType = s.parse().unwrap();
5130            assert_eq!(parsed, mt);
5131        }
5132    }
5133
5134    // -- Channel type parsing --
5135
5136    #[test]
5137    fn test_channel_type_roundtrip() {
5138        let types = vec![
5139            ChannelType::Direct,
5140            ChannelType::Group,
5141            ChannelType::Broadcast,
5142            ChannelType::PubSub,
5143        ];
5144        for ct in types {
5145            let s = ct.to_string();
5146            let parsed: ChannelType = s.parse().unwrap();
5147            assert_eq!(parsed, ct);
5148        }
5149    }
5150
5151    // ===================================================================
5152    // NEW TESTS — Features 1-10
5153    // ===================================================================
5154
5155    // -- test_message_priority_ordering --
5156
5157    #[test]
5158    fn test_message_priority_ordering() {
5159        // MessagePriority derives Ord, so we can sort by priority
5160        let mut priorities = vec![
5161            MessagePriority::Critical,
5162            MessagePriority::Low,
5163            MessagePriority::Urgent,
5164            MessagePriority::Normal,
5165            MessagePriority::High,
5166        ];
5167        priorities.sort();
5168        assert_eq!(
5169            priorities,
5170            vec![
5171                MessagePriority::Low,
5172                MessagePriority::Normal,
5173                MessagePriority::High,
5174                MessagePriority::Urgent,
5175                MessagePriority::Critical,
5176            ]
5177        );
5178    }
5179
5180    // -- test_channel_state_pause_blocks_send --
5181
5182    #[test]
5183    fn test_channel_state_pause_blocks_send() {
5184        let (mut store, cid) = new_store_with_channel();
5185        store.pause_channel(cid).unwrap();
5186        let result = store.send_message(cid, "alice", "blocked", MessageType::Text);
5187        assert!(result.is_err());
5188        // Should have dead-lettered it
5189        assert_eq!(store.dead_letter_count(), 1);
5190    }
5191
5192    // -- test_channel_state_drain_allows_receive --
5193
5194    #[test]
5195    fn test_channel_state_drain_allows_receive() {
5196        let (mut store, cid) = new_store_with_channel();
5197        // Send a message while active
5198        store
5199            .send_message(cid, "alice", "before drain", MessageType::Text)
5200            .unwrap();
5201        // Now drain the channel
5202        store.drain_channel(cid).unwrap();
5203        // Receive should still work
5204        let msgs = store.receive_messages(cid, None, None).unwrap();
5205        assert_eq!(msgs.len(), 1);
5206        // But sending should fail
5207        let result = store.send_message(cid, "bob", "blocked", MessageType::Text);
5208        assert!(result.is_err());
5209    }
5210
5211    // -- test_channel_state_close_blocks_all --
5212
5213    #[test]
5214    fn test_channel_state_close_blocks_all() {
5215        let (mut store, cid) = new_store_with_channel();
5216        store
5217            .send_message(cid, "alice", "before close", MessageType::Text)
5218            .unwrap();
5219        store.close_channel(cid).unwrap();
5220        // Send should fail
5221        let send_result = store.send_message(cid, "bob", "nope", MessageType::Text);
5222        assert!(send_result.is_err());
5223        // Receive should also fail
5224        let recv_result = store.receive_messages(cid, None, None);
5225        assert!(recv_result.is_err());
5226    }
5227
5228    // -- test_channel_resume_after_pause --
5229
5230    #[test]
5231    fn test_channel_resume_after_pause() {
5232        let (mut store, cid) = new_store_with_channel();
5233        store.pause_channel(cid).unwrap();
5234        let ch = store.get_channel(cid).unwrap();
5235        assert_eq!(ch.state, ChannelState::Paused);
5236
5237        store.resume_channel(cid).unwrap();
5238        let ch = store.get_channel(cid).unwrap();
5239        assert_eq!(ch.state, ChannelState::Active);
5240
5241        // Should be able to send again
5242        let msg = store
5243            .send_message(cid, "alice", "resumed", MessageType::Text)
5244            .unwrap();
5245        assert_eq!(msg.content, "resumed");
5246    }
5247
5248    // -- test_send_reply --
5249
5250    #[test]
5251    fn test_send_reply() {
5252        let (mut store, cid) = new_store_with_channel();
5253        let parent = store
5254            .send_message(cid, "alice", "original question", MessageType::Query)
5255            .unwrap();
5256        let reply = store
5257            .send_reply(cid, parent.id, "bob", "answer here", MessageType::Response)
5258            .unwrap();
5259        assert_eq!(reply.reply_to, Some(parent.id));
5260        assert!(reply.thread_id.is_some());
5261        // Parent should also have a thread_id now
5262        let updated_parent = store.get_message(parent.id).unwrap();
5263        assert!(updated_parent.thread_id.is_some());
5264        assert_eq!(updated_parent.thread_id, reply.thread_id);
5265    }
5266
5267    // -- test_get_thread --
5268
5269    #[test]
5270    fn test_get_thread() {
5271        let (mut store, cid) = new_store_with_channel();
5272        let parent = store
5273            .send_message(cid, "alice", "start thread", MessageType::Text)
5274            .unwrap();
5275        let r1 = store
5276            .send_reply(cid, parent.id, "bob", "reply 1", MessageType::Response)
5277            .unwrap();
5278        let thread_id = r1.thread_id.clone().unwrap();
5279        store
5280            .send_reply(cid, parent.id, "carol", "reply 2", MessageType::Response)
5281            .unwrap();
5282
5283        let thread = store.get_thread(&thread_id);
5284        // Parent + 2 replies = 3 messages in the thread
5285        assert_eq!(thread.len(), 3);
5286        // Ordered by timestamp
5287        assert!(thread[0].timestamp <= thread[1].timestamp);
5288        assert!(thread[1].timestamp <= thread[2].timestamp);
5289    }
5290
5291    // -- test_get_replies --
5292
5293    #[test]
5294    fn test_get_replies() {
5295        let (mut store, cid) = new_store_with_channel();
5296        let parent = store
5297            .send_message(cid, "alice", "parent msg", MessageType::Text)
5298            .unwrap();
5299        store
5300            .send_reply(cid, parent.id, "bob", "reply A", MessageType::Response)
5301            .unwrap();
5302        store
5303            .send_reply(cid, parent.id, "carol", "reply B", MessageType::Response)
5304            .unwrap();
5305        // Also send a non-reply message
5306        store
5307            .send_message(cid, "dave", "unrelated", MessageType::Text)
5308            .unwrap();
5309
5310        let replies = store.get_replies(parent.id);
5311        assert_eq!(replies.len(), 2);
5312        assert!(replies.iter().all(|r| r.reply_to == Some(parent.id)));
5313    }
5314
5315    // -- test_dead_letter_on_closed_channel --
5316
5317    #[test]
5318    fn test_dead_letter_on_closed_channel() {
5319        let (mut store, cid) = new_store_with_channel();
5320        store.close_channel(cid).unwrap();
5321
5322        let result = store.send_message(cid, "alice", "dropped", MessageType::Text);
5323        assert!(result.is_err());
5324        assert_eq!(store.dead_letter_count(), 1);
5325
5326        let dls = store.list_dead_letters();
5327        assert_eq!(dls.len(), 1);
5328        assert_eq!(dls[0].original_message.content, "dropped");
5329        assert_eq!(dls[0].reason, DeadLetterReason::ChannelClosed);
5330    }
5331
5332    // -- test_dead_letter_replay --
5333
5334    #[test]
5335    fn test_dead_letter_replay() {
5336        let (mut store, cid) = new_store_with_channel();
5337        store.close_channel(cid).unwrap();
5338
5339        // This will fail and dead-letter
5340        let _ = store.send_message(cid, "alice", "retry me", MessageType::Text);
5341        assert_eq!(store.dead_letter_count(), 1);
5342
5343        // Reopen the channel
5344        store.resume_channel(cid).unwrap();
5345
5346        // Replay the dead letter
5347        let msg = store.replay_dead_letter(0).unwrap();
5348        assert_eq!(msg.content, "retry me");
5349        assert_eq!(msg.status, MessageStatus::Sent);
5350        // Dead letter should be removed after successful replay
5351        assert_eq!(store.dead_letter_count(), 0);
5352    }
5353
5354    // -- test_expire_messages --
5355
5356    #[test]
5357    fn test_expire_messages() {
5358        let mut store = CommStore::new();
5359        let config = ChannelConfig {
5360            ttl_seconds: 1, // 1 second TTL
5361            ..Default::default()
5362        };
5363        let ch = store
5364            .create_channel("ephemeral", ChannelType::Group, Some(config))
5365            .unwrap();
5366
5367        // Insert a message with an old timestamp by directly manipulating
5368        let id = 100;
5369        let old_time = Utc::now() - chrono::Duration::seconds(10);
5370        let msg = Message {
5371            id,
5372            channel_id: ch.id,
5373            sender: "alice".to_string(),
5374            recipient: None,
5375            content: "old message".to_string(),
5376            message_type: MessageType::Text,
5377            timestamp: old_time,
5378            metadata: HashMap::new(),
5379            signature: None,
5380            acknowledged_by: Vec::new(),
5381            status: MessageStatus::Sent,
5382            priority: MessagePriority::Normal,
5383            reply_to: None,
5384            correlation_id: None,
5385            thread_id: None,
5386            comm_timestamp: CommTimestamp::default(),
5387            rich_content_json: None,
5388            comm_id: None,
5389            receipt_id: None,
5390        };
5391        store.messages.insert(id, msg);
5392
5393        // Also add a fresh message
5394        store
5395            .send_message(ch.id, "bob", "fresh message", MessageType::Text)
5396            .unwrap();
5397
5398        let expired_count = store.expire_messages();
5399        assert_eq!(expired_count, 1);
5400        // The old message should be gone from messages
5401        assert!(store.get_message(100).is_none());
5402        // But should be in dead letters
5403        assert_eq!(store.dead_letter_count(), 1);
5404        let dls = store.list_dead_letters();
5405        assert_eq!(dls[0].reason, DeadLetterReason::Expired);
5406        // The fresh message should still be there
5407        assert_eq!(store.messages.len(), 1);
5408    }
5409
5410    // -- test_compact_removes_closed_channel_messages --
5411
5412    #[test]
5413    fn test_compact_removes_closed_channel_messages() {
5414        let (mut store, cid) = new_store_with_channel();
5415        store
5416            .send_message(cid, "alice", "msg1", MessageType::Text)
5417            .unwrap();
5418        store
5419            .send_message(cid, "alice", "msg2", MessageType::Text)
5420            .unwrap();
5421
5422        // Create another active channel with a message
5423        let ch2 = store
5424            .create_channel("active-ch", ChannelType::Group, None)
5425            .unwrap();
5426        store
5427            .send_message(ch2.id, "bob", "active msg", MessageType::Text)
5428            .unwrap();
5429
5430        assert_eq!(store.messages.len(), 3);
5431
5432        // Close the first channel
5433        store.close_channel(cid).unwrap();
5434
5435        let removed = store.compact();
5436        assert_eq!(removed, 2); // 2 messages from closed channel
5437        assert_eq!(store.messages.len(), 1); // only the active channel message remains
5438    }
5439
5440    // -- test_delivery_mode_default --
5441
5442    #[test]
5443    fn test_delivery_mode_default() {
5444        let config = ChannelConfig::default();
5445        assert_eq!(config.delivery_mode, DeliveryMode::AtLeastOnce);
5446        assert_eq!(config.retention_policy, RetentionPolicy::Forever);
5447    }
5448
5449    // -- test_enhanced_stats --
5450
5451    #[test]
5452    fn test_enhanced_stats() {
5453        let (mut store, cid) = new_store_with_channel();
5454        store
5455            .send_message(cid, "alice", "text msg", MessageType::Text)
5456            .unwrap();
5457        store
5458            .send_message(cid, "bob", "command msg", MessageType::Command)
5459            .unwrap();
5460        store
5461            .send_message_with_priority(
5462                cid,
5463                "carol",
5464                "urgent msg",
5465                MessageType::Text,
5466                MessagePriority::Urgent,
5467            )
5468            .unwrap();
5469
5470        // Close a channel to get dead letters
5471        let ch2 = store
5472            .create_channel("closable", ChannelType::Group, None)
5473            .unwrap();
5474        store.close_channel(ch2.id).unwrap();
5475        let _ = store.send_message(ch2.id, "dave", "dropped", MessageType::Text);
5476
5477        let stats = store.stats();
5478        assert_eq!(stats.channel_count, 2);
5479        assert_eq!(stats.message_count, 3);
5480        assert_eq!(stats.dead_letter_count, 1);
5481
5482        // messages_by_type: 2 text, 1 command
5483        assert_eq!(stats.messages_by_type.get("text"), Some(&2));
5484        assert_eq!(stats.messages_by_type.get("command"), Some(&1));
5485
5486        // messages_by_priority: 2 normal, 1 urgent
5487        assert_eq!(stats.messages_by_priority.get("normal"), Some(&2));
5488        assert_eq!(stats.messages_by_priority.get("urgent"), Some(&1));
5489
5490        // channels_by_state: 1 active, 1 closed
5491        assert_eq!(stats.channels_by_state.get("active"), Some(&1));
5492        assert_eq!(stats.channels_by_state.get("closed"), Some(&1));
5493
5494        // oldest/newest should be set
5495        assert!(stats.oldest_message.is_some());
5496        assert!(stats.newest_message.is_some());
5497    }
5498
5499    // -- test_message_status_transitions --
5500
5501    #[test]
5502    fn test_message_status_transitions() {
5503        let (mut store, cid) = new_store_with_channel();
5504
5505        // Message starts as Sent (set during send_message)
5506        let msg = store
5507            .send_message(cid, "alice", "track me", MessageType::Text)
5508            .unwrap();
5509        assert_eq!(msg.status, MessageStatus::Sent);
5510
5511        // After acknowledgment, status becomes Acknowledged
5512        store.acknowledge_message(msg.id, "bob").unwrap();
5513        let updated = store.get_message(msg.id).unwrap();
5514        assert_eq!(updated.status, MessageStatus::Acknowledged);
5515
5516        // Default status is Created
5517        assert_eq!(MessageStatus::default(), MessageStatus::Created);
5518    }
5519
5520    // -- test_send_message_with_priority --
5521
5522    #[test]
5523    fn test_send_message_with_priority() {
5524        let (mut store, cid) = new_store_with_channel();
5525        let msg = store
5526            .send_message_with_priority(
5527                cid,
5528                "alice",
5529                "critical alert",
5530                MessageType::Notification,
5531                MessagePriority::Critical,
5532            )
5533            .unwrap();
5534        assert_eq!(msg.priority, MessagePriority::Critical);
5535
5536        // Verify stored message also has the priority
5537        let stored = store.get_message(msg.id).unwrap();
5538        assert_eq!(stored.priority, MessagePriority::Critical);
5539    }
5540
5541    // -- test_dead_letter_clear --
5542
5543    #[test]
5544    fn test_dead_letter_clear() {
5545        let (mut store, cid) = new_store_with_channel();
5546        store.close_channel(cid).unwrap();
5547        let _ = store.send_message(cid, "alice", "dl1", MessageType::Text);
5548        let _ = store.send_message(cid, "alice", "dl2", MessageType::Text);
5549        assert_eq!(store.dead_letter_count(), 2);
5550
5551        store.clear_dead_letters();
5552        assert_eq!(store.dead_letter_count(), 0);
5553    }
5554
5555    // -- test_compact_enforces_retention_policy --
5556
5557    #[test]
5558    fn test_compact_enforces_retention_policy() {
5559        let mut store = CommStore::new();
5560        let config = ChannelConfig {
5561            retention_policy: RetentionPolicy::MessageCount(2),
5562            ..Default::default()
5563        };
5564        let ch = store
5565            .create_channel("limited", ChannelType::Group, Some(config))
5566            .unwrap();
5567
5568        // Send 5 messages
5569        for i in 0..5 {
5570            store
5571                .send_message(ch.id, "alice", &format!("msg-{i}"), MessageType::Text)
5572                .unwrap();
5573        }
5574        assert_eq!(store.messages.len(), 5);
5575
5576        let removed = store.compact();
5577        assert_eq!(removed, 3); // 5 - 2 = 3 removed
5578        assert_eq!(store.messages.len(), 2);
5579    }
5580
5581    // -- test_save_and_load_with_new_fields --
5582
5583    #[test]
5584    fn test_save_and_load_with_new_fields() {
5585        let (mut store, cid) = new_store_with_channel();
5586        store.join_channel(cid, "alice").unwrap();
5587
5588        // Use new features
5589        let msg = store
5590            .send_message_with_priority(
5591                cid,
5592                "alice",
5593                "priority msg",
5594                MessageType::Text,
5595                MessagePriority::High,
5596            )
5597            .unwrap();
5598        store
5599            .send_reply(cid, msg.id, "bob", "reply", MessageType::Response)
5600            .unwrap();
5601
5602        // Close another channel to create dead letters
5603        let ch2 = store
5604            .create_channel("closing", ChannelType::Group, None)
5605            .unwrap();
5606        store.close_channel(ch2.id).unwrap();
5607        let _ = store.send_message(ch2.id, "carol", "dead", MessageType::Text);
5608
5609        let dir = tempfile::tempdir().unwrap();
5610        let path = dir.path().join("test_new.acomm");
5611        store.save(&path).unwrap();
5612
5613        let loaded = CommStore::load(&path).unwrap();
5614        assert_eq!(loaded.channels.len(), 2);
5615        assert_eq!(loaded.messages.len(), 2);
5616        assert_eq!(loaded.dead_letters.len(), 1);
5617
5618        // Verify new fields survived round-trip
5619        let loaded_msg = loaded.get_message(msg.id).unwrap();
5620        assert_eq!(loaded_msg.priority, MessagePriority::High);
5621
5622        let ch2_loaded = loaded.get_channel(ch2.id).unwrap();
5623        assert_eq!(ch2_loaded.state, ChannelState::Closed);
5624    }
5625
5626    // -- test_channel_state_display --
5627
5628    #[test]
5629    fn test_channel_state_display() {
5630        assert_eq!(ChannelState::Active.to_string(), "active");
5631        assert_eq!(ChannelState::Paused.to_string(), "paused");
5632        assert_eq!(ChannelState::Draining.to_string(), "draining");
5633        assert_eq!(ChannelState::Closed.to_string(), "closed");
5634    }
5635
5636    // -- test_dead_letter_on_nonexistent_channel --
5637
5638    #[test]
5639    fn test_dead_letter_on_nonexistent_channel() {
5640        let mut store = CommStore::new();
5641        let result = store.send_message(999, "alice", "nowhere", MessageType::Text);
5642        assert!(result.is_err());
5643        assert_eq!(store.dead_letter_count(), 1);
5644        let dls = store.list_dead_letters();
5645        assert_eq!(dls[0].reason, DeadLetterReason::ChannelNotFound);
5646    }
5647
5648    // --- Consent tests ---
5649
5650    #[test]
5651    fn consent_grant_and_check() {
5652        let mut store = CommStore::new();
5653        store
5654            .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
5655            .unwrap();
5656        assert!(store.check_consent("alice", "bob", &ConsentScope::ReadMessages));
5657        assert!(!store.check_consent("bob", "alice", &ConsentScope::ReadMessages));
5658    }
5659
5660    #[test]
5661    fn consent_revoke() {
5662        let mut store = CommStore::new();
5663        store
5664            .grant_consent("alice", "bob", ConsentScope::SendMessages, None, None)
5665            .unwrap();
5666        assert!(store.check_consent("alice", "bob", &ConsentScope::SendMessages));
5667        store
5668            .revoke_consent("alice", "bob", &ConsentScope::SendMessages)
5669            .unwrap();
5670        assert!(!store.check_consent("alice", "bob", &ConsentScope::SendMessages));
5671    }
5672
5673    #[test]
5674    fn consent_list_filtered() {
5675        let mut store = CommStore::new();
5676        store.grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None).unwrap();
5677        store.grant_consent("charlie", "bob", ConsentScope::SendMessages, None, None).unwrap();
5678        assert_eq!(store.list_consent_gates(Some("bob")).len(), 2);
5679        assert_eq!(store.list_consent_gates(Some("alice")).len(), 1);
5680        assert_eq!(store.list_consent_gates(None).len(), 2);
5681    }
5682
5683    // --- Trust tests ---
5684
5685    #[test]
5686    fn trust_set_and_get() {
5687        let mut store = CommStore::new();
5688        assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Standard);
5689        store.set_trust_level("agent-1", CommTrustLevel::High).unwrap();
5690        assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::High);
5691    }
5692
5693    #[test]
5694    fn trust_list() {
5695        let mut store = CommStore::new();
5696        store.set_trust_level("a", CommTrustLevel::Full).unwrap();
5697        store.set_trust_level("b", CommTrustLevel::Minimal).unwrap();
5698        assert_eq!(store.list_trust_levels().len(), 2);
5699    }
5700
5701    // --- Temporal tests ---
5702
5703    #[test]
5704    fn temporal_schedule_and_list() {
5705        let (mut store, ch_id) = new_store_with_channel();
5706        store.join_channel(ch_id, "alice").unwrap();
5707        store
5708            .schedule_message(ch_id, "alice", "hello future", TemporalTarget::Immediate, None)
5709            .unwrap();
5710        assert_eq!(store.list_scheduled().len(), 1);
5711    }
5712
5713    #[test]
5714    fn temporal_cancel() {
5715        let (mut store, ch_id) = new_store_with_channel();
5716        store.join_channel(ch_id, "alice").unwrap();
5717        let msg = store
5718            .schedule_message(ch_id, "alice", "later", TemporalTarget::FutureRelative { delay_seconds: 3600 }, None)
5719            .unwrap();
5720        let tid = msg.id;
5721        assert_eq!(store.list_scheduled().len(), 1);
5722        store.cancel_scheduled(tid).unwrap();
5723        assert_eq!(store.list_scheduled().len(), 0);
5724    }
5725
5726    #[test]
5727    fn temporal_deliver_immediate() {
5728        let (mut store, ch_id) = new_store_with_channel();
5729        store.join_channel(ch_id, "alice").unwrap();
5730        store
5731            .schedule_message(ch_id, "alice", "now!", TemporalTarget::Immediate, None)
5732            .unwrap();
5733        let delivered = store.deliver_pending_temporal();
5734        assert_eq!(delivered, 1);
5735        assert_eq!(store.list_scheduled().len(), 0);
5736    }
5737
5738    // --- Federation tests ---
5739
5740    #[test]
5741    fn federation_configure() {
5742        let mut store = CommStore::new();
5743        store
5744            .configure_federation(true, "zone-a", FederationPolicy::Allow)
5745            .unwrap();
5746        let config = store.get_federation_config();
5747        assert!(config.enabled);
5748        assert_eq!(config.local_zone, "zone-a");
5749    }
5750
5751    #[test]
5752    fn federation_add_remove_zone() {
5753        let mut store = CommStore::new();
5754        store.add_federated_zone(FederatedZone {
5755            zone_id: "zone-b".to_string(),
5756            name: "Zone B".to_string(),
5757            endpoint: "https://b.example.com".to_string(),
5758            policy: FederationPolicy::Allow,
5759            trust_level: CommTrustLevel::High,
5760        }).unwrap();
5761        assert_eq!(store.list_federated_zones().len(), 1);
5762        store.remove_federated_zone("zone-b").unwrap();
5763        assert_eq!(store.list_federated_zones().len(), 0);
5764    }
5765
5766    #[test]
5767    fn federation_duplicate_zone_error() {
5768        let mut store = CommStore::new();
5769        let zone = FederatedZone {
5770            zone_id: "z1".to_string(),
5771            name: "Z1".to_string(),
5772            endpoint: String::new(),
5773            policy: FederationPolicy::Deny,
5774            trust_level: CommTrustLevel::Basic,
5775        };
5776        store.add_federated_zone(zone.clone()).unwrap();
5777        assert!(store.add_federated_zone(zone).is_err());
5778    }
5779
5780    // --- Hive mind tests ---
5781
5782    #[test]
5783    fn hive_form_and_list() {
5784        let mut store = CommStore::new();
5785        store.form_hive("test-hive", "alice", CollectiveDecisionMode::Majority).unwrap();
5786        assert_eq!(store.list_hives().len(), 1);
5787    }
5788
5789    #[test]
5790    fn hive_join_and_leave() {
5791        let mut store = CommStore::new();
5792        let hive = store.form_hive("h1", "alice", CollectiveDecisionMode::Consensus).unwrap();
5793        let hid = hive.id;
5794        store.join_hive(hid, "bob", HiveRole::Member).unwrap();
5795        assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 2);
5796        store.leave_hive(hid, "bob").unwrap();
5797        assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 1);
5798    }
5799
5800    #[test]
5801    fn hive_dissolve() {
5802        let mut store = CommStore::new();
5803        let hive = store.form_hive("h2", "alice", CollectiveDecisionMode::CoordinatorDecides).unwrap();
5804        let hid = hive.id;
5805        store.dissolve_hive(hid).unwrap();
5806        assert!(store.get_hive(hid).is_none());
5807    }
5808
5809    #[test]
5810    fn hive_join_duplicate_error() {
5811        let mut store = CommStore::new();
5812        let hive = store.form_hive("h3", "alice", CollectiveDecisionMode::Unanimous).unwrap();
5813        let hid = hive.id;
5814        assert!(store.join_hive(hid, "alice", HiveRole::Member).is_err());
5815    }
5816
5817    // --- Communication log tests ---
5818
5819    #[test]
5820    fn comm_log_entries() {
5821        let mut store = CommStore::new();
5822        store.log_communication("hello", "user", Some("greeting".to_string()), None, None);
5823        store.log_communication("hi back", "agent", Some("greeting".to_string()), None, None);
5824        assert_eq!(store.get_comm_log(None).len(), 2);
5825        assert_eq!(store.get_comm_log(Some(1)).len(), 1);
5826    }
5827
5828    // --- Grounding tests ---
5829
5830    #[test]
5831    fn grounding_verified_channel() {
5832        let (store, _ch_id) = new_store_with_channel();
5833        let result = store.ground_claim("test-channel exists");
5834        assert_eq!(result.status, GroundingStatus::Verified);
5835        assert!(!result.evidence.is_empty());
5836    }
5837
5838    #[test]
5839    fn grounding_ungrounded() {
5840        let store = CommStore::new();
5841        let result = store.ground_claim("nonexistent-thing");
5842        assert_eq!(result.status, GroundingStatus::Ungrounded);
5843        assert!(result.evidence.is_empty());
5844    }
5845
5846    #[test]
5847    fn grounding_trust_evidence() {
5848        let mut store = CommStore::new();
5849        store.set_trust_level("agent-x", CommTrustLevel::High).unwrap();
5850        let result = store.ground_claim("agent-x has trust");
5851        assert_ne!(result.status, GroundingStatus::Ungrounded);
5852    }
5853
5854    // --- Stats tests ---
5855
5856    #[test]
5857    fn stats_include_new_fields() {
5858        let mut store = CommStore::new();
5859        store.grant_consent("a", "b", ConsentScope::ReadMessages, None, None).unwrap();
5860        store.set_trust_level("x", CommTrustLevel::Full).unwrap();
5861        store.form_hive("h", "coord", CollectiveDecisionMode::Majority).unwrap();
5862        let stats = store.stats();
5863        assert_eq!(stats.consent_gate_count, 1);
5864        assert_eq!(stats.trust_override_count, 1);
5865        assert_eq!(stats.hive_count, 1);
5866    }
5867
5868    // --- Affect messaging test ---
5869
5870    #[test]
5871    fn affect_message_send() {
5872        let (mut store, ch_id) = new_store_with_channel();
5873        store.join_channel(ch_id, "alice").unwrap();
5874        let msg = store
5875            .send_affect_message(
5876                ch_id,
5877                "alice",
5878                "I'm excited!",
5879                AffectState {
5880                    valence: 0.8,
5881                    arousal: 0.9,
5882                    ..Default::default()
5883                },
5884            )
5885            .unwrap();
5886        assert!(msg.content.contains("[affect:"));
5887        assert!(msg.content.contains("I'm excited!"));
5888    }
5889
5890    // ===================================================================
5891    // NEW TESTS — Consent, Trust, Temporal, Federation, Hive, Grounding,
5892    //             Comm Log, Stats, Save/Load round-trips (30 tests)
5893    // ===================================================================
5894
5895    // --- Consent tests ---
5896
5897    #[test]
5898    fn consent_grant_with_reason_and_expiry() {
5899        let mut store = CommStore::new();
5900        let entry = store
5901            .grant_consent(
5902                "alice",
5903                "bob",
5904                ConsentScope::ScheduleMessages,
5905                Some("project collaboration".to_string()),
5906                Some("2030-12-31T23:59:59Z".to_string()),
5907            )
5908            .unwrap();
5909        assert_eq!(entry.grantor, "alice");
5910        assert_eq!(entry.grantee, "bob");
5911        assert_eq!(entry.scope, ConsentScope::ScheduleMessages);
5912        assert_eq!(entry.status, ConsentStatus::Granted);
5913        assert_eq!(entry.reason.as_deref(), Some("project collaboration"));
5914        assert_eq!(entry.expires_at.as_deref(), Some("2030-12-31T23:59:59Z"));
5915    }
5916
5917    #[test]
5918    fn consent_update_existing() {
5919        let mut store = CommStore::new();
5920        // First grant with no reason
5921        store
5922            .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
5923            .unwrap();
5924        assert!(store.check_consent("alice", "bob", &ConsentScope::ReadMessages));
5925
5926        // Grant again with reason and expiry — should update, not duplicate
5927        store
5928            .grant_consent(
5929                "alice",
5930                "bob",
5931                ConsentScope::ReadMessages,
5932                Some("updated reason".to_string()),
5933                Some("2031-01-01T00:00:00Z".to_string()),
5934            )
5935            .unwrap();
5936
5937        // Should still be only one entry for this (grantor, grantee, scope) triple
5938        let gates = store.list_consent_gates(Some("alice"));
5939        let matching: Vec<_> = gates
5940            .iter()
5941            .filter(|e| {
5942                e.grantor == "alice"
5943                    && e.grantee == "bob"
5944                    && e.scope == ConsentScope::ReadMessages
5945            })
5946            .collect();
5947        assert_eq!(matching.len(), 1, "Should update existing, not create duplicate");
5948        assert_eq!(matching[0].reason.as_deref(), Some("updated reason"));
5949    }
5950
5951    #[test]
5952    fn consent_revoke_nonexistent_error() {
5953        let mut store = CommStore::new();
5954        let result = store.revoke_consent("alice", "bob", &ConsentScope::Federate);
5955        assert!(result.is_err(), "Revoking non-existent consent should fail");
5956    }
5957
5958    #[test]
5959    fn consent_empty_grantor_error() {
5960        let mut store = CommStore::new();
5961        let result = store.grant_consent("", "bob", ConsentScope::ReadMessages, None, None);
5962        assert!(result.is_err(), "Empty grantor should return error");
5963    }
5964
5965    #[test]
5966    fn consent_list_empty_returns_empty() {
5967        let store = CommStore::new();
5968        let gates = store.list_consent_gates(None);
5969        assert!(gates.is_empty(), "Fresh store should have no consent gates");
5970        let gates_filtered = store.list_consent_gates(Some("nobody"));
5971        assert!(gates_filtered.is_empty());
5972    }
5973
5974    // --- Trust tests ---
5975
5976    #[test]
5977    fn trust_empty_agent_error() {
5978        let mut store = CommStore::new();
5979        let result = store.set_trust_level("", CommTrustLevel::High);
5980        assert!(result.is_err(), "Empty agent_id should return error");
5981    }
5982
5983    #[test]
5984    fn trust_override_existing() {
5985        let mut store = CommStore::new();
5986        store.set_trust_level("agent-1", CommTrustLevel::Basic).unwrap();
5987        assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Basic);
5988        store.set_trust_level("agent-1", CommTrustLevel::Absolute).unwrap();
5989        assert_eq!(store.get_trust_level("agent-1"), CommTrustLevel::Absolute);
5990        // Should still be only one entry
5991        assert_eq!(store.list_trust_levels().len(), 1);
5992    }
5993
5994    #[test]
5995    fn trust_default_is_standard() {
5996        let store = CommStore::new();
5997        assert_eq!(
5998            store.get_trust_level("unknown-agent"),
5999            CommTrustLevel::Standard,
6000            "Fresh agent should default to Standard trust"
6001        );
6002    }
6003
6004    // --- Temporal tests ---
6005
6006    #[test]
6007    fn temporal_schedule_to_nonexistent_channel() {
6008        let mut store = CommStore::new();
6009        let result = store.schedule_message(
6010            999,
6011            "alice",
6012            "hello",
6013            TemporalTarget::Immediate,
6014            None,
6015        );
6016        assert!(result.is_err(), "Scheduling to non-existent channel should fail");
6017    }
6018
6019    #[test]
6020    fn temporal_cancel_delivered_error() {
6021        let (mut store, ch_id) = new_store_with_channel();
6022        let msg = store
6023            .schedule_message(ch_id, "alice", "now", TemporalTarget::Immediate, None)
6024            .unwrap();
6025        let tid = msg.id;
6026
6027        // Deliver it
6028        let delivered = store.deliver_pending_temporal();
6029        assert_eq!(delivered, 1);
6030
6031        // Try to cancel the already-delivered message
6032        let result = store.cancel_scheduled(tid);
6033        assert!(result.is_err(), "Cannot cancel already-delivered message");
6034    }
6035
6036    #[test]
6037    fn temporal_cancel_nonexistent_error() {
6038        let mut store = CommStore::new();
6039        let result = store.cancel_scheduled(9999);
6040        assert!(result.is_err(), "Cancelling non-existent temporal ID should fail");
6041    }
6042
6043    #[test]
6044    fn temporal_deliver_future_relative_not_delivered() {
6045        let (mut store, ch_id) = new_store_with_channel();
6046        store
6047            .schedule_message(
6048                ch_id,
6049                "alice",
6050                "later msg",
6051                TemporalTarget::FutureRelative { delay_seconds: 3600 },
6052                None,
6053            )
6054            .unwrap();
6055
6056        // deliver_pending_temporal only delivers Immediate targets
6057        let delivered = store.deliver_pending_temporal();
6058        assert_eq!(delivered, 0, "FutureRelative messages should not be delivered by deliver_pending_temporal");
6059        assert_eq!(store.list_scheduled().len(), 1, "Message should still be in queue");
6060    }
6061
6062    #[test]
6063    fn temporal_multiple_immediate() {
6064        let (mut store, ch_id) = new_store_with_channel();
6065        for i in 0..5 {
6066            store
6067                .schedule_message(
6068                    ch_id,
6069                    "alice",
6070                    &format!("immediate-{i}"),
6071                    TemporalTarget::Immediate,
6072                    None,
6073                )
6074                .unwrap();
6075        }
6076        assert_eq!(store.list_scheduled().len(), 5);
6077
6078        let delivered = store.deliver_pending_temporal();
6079        assert_eq!(delivered, 5, "All 5 Immediate messages should be delivered");
6080        assert_eq!(store.list_scheduled().len(), 0, "No undelivered messages should remain");
6081        // The delivered messages should be in the message store
6082        assert_eq!(store.messages.len(), 5);
6083    }
6084
6085    // --- Federation tests ---
6086
6087    #[test]
6088    fn federation_empty_zone_error() {
6089        let mut store = CommStore::new();
6090        let result = store.configure_federation(true, "", FederationPolicy::Allow);
6091        assert!(result.is_err(), "Empty local_zone should return error");
6092    }
6093
6094    #[test]
6095    fn federation_remove_nonexistent_zone_error() {
6096        let mut store = CommStore::new();
6097        let result = store.remove_federated_zone("does-not-exist");
6098        assert!(result.is_err(), "Removing non-existent zone should return error");
6099    }
6100
6101    #[test]
6102    fn federation_zone_with_trust_level() {
6103        let mut store = CommStore::new();
6104        store
6105            .add_federated_zone(FederatedZone {
6106                zone_id: "trusted-zone".to_string(),
6107                name: "Trusted Zone".to_string(),
6108                endpoint: "https://trusted.example.com".to_string(),
6109                policy: FederationPolicy::Allow,
6110                trust_level: CommTrustLevel::Full,
6111            })
6112            .unwrap();
6113        let zones = store.list_federated_zones();
6114        assert_eq!(zones.len(), 1);
6115        assert_eq!(zones[0].trust_level, CommTrustLevel::Full);
6116        assert_eq!(zones[0].zone_id, "trusted-zone");
6117        assert_eq!(zones[0].policy, FederationPolicy::Allow);
6118    }
6119
6120    // --- Hive tests ---
6121
6122    #[test]
6123    fn hive_form_empty_name_error() {
6124        let mut store = CommStore::new();
6125        let result = store.form_hive("", "alice", CollectiveDecisionMode::Majority);
6126        assert!(result.is_err(), "Empty hive name should return error");
6127    }
6128
6129    #[test]
6130    fn hive_dissolve_nonexistent_error() {
6131        let mut store = CommStore::new();
6132        let result = store.dissolve_hive(9999);
6133        assert!(result.is_err(), "Dissolving non-existent hive should return error");
6134    }
6135
6136    #[test]
6137    fn hive_leave_nonexistent_member_error() {
6138        let mut store = CommStore::new();
6139        let hive = store
6140            .form_hive("test-hive", "alice", CollectiveDecisionMode::Majority)
6141            .unwrap();
6142        let hid = hive.id;
6143        let result = store.leave_hive(hid, "ghost");
6144        assert!(result.is_err(), "Leaving hive when not a member should fail");
6145    }
6146
6147    #[test]
6148    fn hive_multiple_members() {
6149        let mut store = CommStore::new();
6150        let hive = store
6151            .form_hive("big-hive", "coordinator", CollectiveDecisionMode::Consensus)
6152            .unwrap();
6153        let hid = hive.id;
6154        // Coordinator is already the first member
6155        assert_eq!(store.get_hive(hid).unwrap().constituents.len(), 1);
6156
6157        store.join_hive(hid, "agent-a", HiveRole::Member).unwrap();
6158        store.join_hive(hid, "agent-b", HiveRole::Member).unwrap();
6159        store.join_hive(hid, "agent-c", HiveRole::Observer).unwrap();
6160
6161        let hive = store.get_hive(hid).unwrap();
6162        assert_eq!(hive.constituents.len(), 4, "Should have coordinator + 3 members");
6163        // Verify roles
6164        assert_eq!(hive.constituents[0].role, HiveRole::Coordinator);
6165        assert_eq!(hive.constituents[1].role, HiveRole::Member);
6166        assert_eq!(hive.constituents[3].role, HiveRole::Observer);
6167    }
6168
6169    // --- Grounding tests ---
6170
6171    #[test]
6172    fn grounding_message_content() {
6173        let (mut store, ch_id) = new_store_with_channel();
6174        store
6175            .send_message(ch_id, "alice", "the deployment succeeded", MessageType::Text)
6176            .unwrap();
6177        // Ground a claim that references the sender
6178        let result = store.ground_claim("alice sent a message");
6179        assert_ne!(result.status, GroundingStatus::Ungrounded);
6180        assert!(
6181            result.evidence.iter().any(|e| e.evidence_type == "message"),
6182            "Should have message evidence"
6183        );
6184    }
6185
6186    #[test]
6187    fn grounding_hive_name() {
6188        let mut store = CommStore::new();
6189        store
6190            .form_hive("project-alpha", "alice", CollectiveDecisionMode::Majority)
6191            .unwrap();
6192        let result = store.ground_claim("project-alpha hive exists");
6193        assert_eq!(result.status, GroundingStatus::Verified);
6194        assert!(
6195            result.evidence.iter().any(|e| e.evidence_type == "hive"),
6196            "Should have hive evidence"
6197        );
6198    }
6199
6200    #[test]
6201    fn grounding_consent_evidence() {
6202        let mut store = CommStore::new();
6203        store
6204            .grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None)
6205            .unwrap();
6206        let result = store.ground_claim("alice has granted consent");
6207        assert_ne!(result.status, GroundingStatus::Ungrounded);
6208        assert!(
6209            result.evidence.iter().any(|e| e.evidence_type == "consent"),
6210            "Should have consent evidence"
6211        );
6212    }
6213
6214    // --- Communication log tests ---
6215
6216    #[test]
6217    fn comm_log_with_affect() {
6218        let mut store = CommStore::new();
6219        let affect = AffectState {
6220            valence: 0.7,
6221            arousal: 0.5,
6222            dominance: 0.6,
6223            emotions: vec![Emotion::Joy, Emotion::Excitement],
6224            urgency: UrgencyLevel::High,
6225            meta_confidence: 0.9,
6226        };
6227        let entry = store.log_communication(
6228            "Great progress today!",
6229            "agent",
6230            Some("status-update".to_string()),
6231            None,
6232            Some(affect),
6233        );
6234        assert_eq!(entry.content, "Great progress today!");
6235        assert_eq!(entry.role, "agent");
6236        assert_eq!(entry.topic.as_deref(), Some("status-update"));
6237        assert!(entry.affect.is_some());
6238        let stored_affect = entry.affect.as_ref().unwrap();
6239        assert_eq!(stored_affect.valence, 0.7);
6240        assert_eq!(stored_affect.emotions.len(), 2);
6241    }
6242
6243    #[test]
6244    fn comm_log_limit() {
6245        let mut store = CommStore::new();
6246        for i in 0..10 {
6247            store.log_communication(
6248                &format!("entry-{i}"),
6249                "user",
6250                None,
6251                None,
6252                None,
6253            );
6254        }
6255        assert_eq!(store.get_comm_log(None).len(), 10, "All 10 entries should be present");
6256        let last_3 = store.get_comm_log(Some(3));
6257        assert_eq!(last_3.len(), 3, "Should return last 3 entries");
6258        assert_eq!(last_3[0].content, "entry-7");
6259        assert_eq!(last_3[1].content, "entry-8");
6260        assert_eq!(last_3[2].content, "entry-9");
6261    }
6262
6263    // --- Stats tests ---
6264
6265    #[test]
6266    fn stats_comprehensive() {
6267        let mut store = CommStore::new();
6268
6269        // Create channels
6270        let ch1 = store.create_channel("chan-1", ChannelType::Group, None).unwrap();
6271        let ch2 = store.create_channel("chan-2", ChannelType::Direct, None).unwrap();
6272        store.join_channel(ch1.id, "alice").unwrap();
6273        store.join_channel(ch1.id, "bob").unwrap();
6274        store.join_channel(ch2.id, "carol").unwrap();
6275
6276        // Send messages
6277        store.send_message(ch1.id, "alice", "msg1", MessageType::Text).unwrap();
6278        store.send_message(ch1.id, "bob", "msg2", MessageType::Command).unwrap();
6279        store.send_message(ch2.id, "carol", "msg3", MessageType::Query).unwrap();
6280
6281        // Add consent
6282        store.grant_consent("alice", "bob", ConsentScope::ReadMessages, None, None).unwrap();
6283        store.grant_consent("bob", "carol", ConsentScope::SendMessages, None, None).unwrap();
6284
6285        // Add trust overrides
6286        store.set_trust_level("alice", CommTrustLevel::High).unwrap();
6287
6288        // Form a hive
6289        store.form_hive("stats-hive", "alice", CollectiveDecisionMode::Majority).unwrap();
6290
6291        // Schedule a temporal message
6292        store.schedule_message(ch1.id, "alice", "future", TemporalTarget::FutureRelative { delay_seconds: 100 }, None).unwrap();
6293
6294        // Configure federation
6295        store.configure_federation(true, "local-zone", FederationPolicy::Allow).unwrap();
6296        store.add_federated_zone(FederatedZone {
6297            zone_id: "remote".to_string(),
6298            name: "Remote Zone".to_string(),
6299            endpoint: String::new(),
6300            policy: FederationPolicy::Selective,
6301            trust_level: CommTrustLevel::Basic,
6302        }).unwrap();
6303
6304        // Add comm log entries
6305        store.log_communication("log1", "user", None, None, None);
6306        store.log_communication("log2", "agent", None, None, None);
6307
6308        let stats = store.stats();
6309        assert_eq!(stats.channel_count, 2);
6310        assert_eq!(stats.message_count, 3);
6311        assert_eq!(stats.total_participants, 3); // alice + bob in ch1, carol in ch2
6312        assert_eq!(stats.consent_gate_count, 2);
6313        assert_eq!(stats.trust_override_count, 1);
6314        assert_eq!(stats.hive_count, 1);
6315        assert_eq!(stats.temporal_queue_count, 1);
6316        assert!(stats.federation_enabled);
6317        assert_eq!(stats.federated_zone_count, 1);
6318        assert_eq!(stats.comm_log_count, 2);
6319        assert_eq!(stats.dead_letter_count, 0);
6320        // Check messages_by_type
6321        assert_eq!(stats.messages_by_type.get("text"), Some(&1));
6322        assert_eq!(stats.messages_by_type.get("command"), Some(&1));
6323        assert_eq!(stats.messages_by_type.get("query"), Some(&1));
6324        // Check channels_by_state
6325        assert_eq!(stats.channels_by_state.get("active"), Some(&2));
6326        // Oldest and newest should be set
6327        assert!(stats.oldest_message.is_some());
6328        assert!(stats.newest_message.is_some());
6329    }
6330
6331    // --- Save/Load round-trip tests ---
6332
6333    #[test]
6334    fn save_load_consent_roundtrip() {
6335        let mut store = CommStore::new();
6336        store
6337            .grant_consent(
6338                "alice",
6339                "bob",
6340                ConsentScope::Federate,
6341                Some("federation partnership".to_string()),
6342                Some("2030-06-15T00:00:00Z".to_string()),
6343            )
6344            .unwrap();
6345        store
6346            .grant_consent("carol", "dave", ConsentScope::HiveParticipation, None, None)
6347            .unwrap();
6348
6349        let dir = tempfile::tempdir().unwrap();
6350        let path = dir.path().join("consent_roundtrip.acomm");
6351        store.save(&path).unwrap();
6352
6353        let loaded = CommStore::load(&path).unwrap();
6354        assert_eq!(loaded.consent_gates.len(), 2);
6355        assert!(loaded.check_consent("alice", "bob", &ConsentScope::Federate));
6356        assert!(loaded.check_consent("carol", "dave", &ConsentScope::HiveParticipation));
6357        // Verify reason survived
6358        let alice_gate = loaded
6359            .consent_gates
6360            .iter()
6361            .find(|e| e.grantor == "alice" && e.grantee == "bob")
6362            .unwrap();
6363        assert_eq!(alice_gate.reason.as_deref(), Some("federation partnership"));
6364        assert_eq!(alice_gate.expires_at.as_deref(), Some("2030-06-15T00:00:00Z"));
6365    }
6366
6367    #[test]
6368    fn save_load_trust_roundtrip() {
6369        let mut store = CommStore::new();
6370        store.set_trust_level("agent-a", CommTrustLevel::Absolute).unwrap();
6371        store.set_trust_level("agent-b", CommTrustLevel::None).unwrap();
6372        store.set_trust_level("agent-c", CommTrustLevel::Minimal).unwrap();
6373
6374        let dir = tempfile::tempdir().unwrap();
6375        let path = dir.path().join("trust_roundtrip.acomm");
6376        store.save(&path).unwrap();
6377
6378        let loaded = CommStore::load(&path).unwrap();
6379        assert_eq!(loaded.list_trust_levels().len(), 3);
6380        assert_eq!(loaded.get_trust_level("agent-a"), CommTrustLevel::Absolute);
6381        assert_eq!(loaded.get_trust_level("agent-b"), CommTrustLevel::None);
6382        assert_eq!(loaded.get_trust_level("agent-c"), CommTrustLevel::Minimal);
6383        // Unknown agent should still return default
6384        assert_eq!(loaded.get_trust_level("unknown"), CommTrustLevel::Standard);
6385    }
6386
6387    #[test]
6388    fn save_load_hive_roundtrip() {
6389        let mut store = CommStore::new();
6390        let hive = store
6391            .form_hive("persistent-hive", "coordinator", CollectiveDecisionMode::Unanimous)
6392            .unwrap();
6393        let hid = hive.id;
6394        store.join_hive(hid, "member-a", HiveRole::Member).unwrap();
6395        store.join_hive(hid, "observer-b", HiveRole::Observer).unwrap();
6396
6397        let dir = tempfile::tempdir().unwrap();
6398        let path = dir.path().join("hive_roundtrip.acomm");
6399        store.save(&path).unwrap();
6400
6401        let loaded = CommStore::load(&path).unwrap();
6402        assert_eq!(loaded.hive_minds.len(), 1);
6403        let loaded_hive = loaded.get_hive(hid).unwrap();
6404        assert_eq!(loaded_hive.name, "persistent-hive");
6405        assert_eq!(loaded_hive.constituents.len(), 3);
6406        assert_eq!(loaded_hive.decision_mode, CollectiveDecisionMode::Unanimous);
6407        assert_eq!(loaded_hive.constituents[0].role, HiveRole::Coordinator);
6408        assert_eq!(loaded_hive.constituents[0].agent_id, "coordinator");
6409        assert_eq!(loaded_hive.constituents[1].agent_id, "member-a");
6410        assert_eq!(loaded_hive.constituents[2].agent_id, "observer-b");
6411    }
6412
6413    #[test]
6414    fn save_load_temporal_roundtrip() {
6415        let mut store = CommStore::new();
6416        let ch = store.create_channel("temporal-ch", ChannelType::Group, None).unwrap();
6417        store
6418            .schedule_message(
6419                ch.id,
6420                "alice",
6421                "deliver later",
6422                TemporalTarget::FutureAbsolute {
6423                    deliver_at: "2030-01-01T00:00:00Z".to_string(),
6424                },
6425                Some(AffectState {
6426                    valence: 0.5,
6427                    arousal: 0.3,
6428                    ..Default::default()
6429                }),
6430            )
6431            .unwrap();
6432        store
6433            .schedule_message(ch.id, "bob", "deliver now", TemporalTarget::Immediate, None)
6434            .unwrap();
6435
6436        let dir = tempfile::tempdir().unwrap();
6437        let path = dir.path().join("temporal_roundtrip.acomm");
6438        store.save(&path).unwrap();
6439
6440        let loaded = CommStore::load(&path).unwrap();
6441        assert_eq!(loaded.temporal_queue.len(), 2);
6442        // Both should be undelivered after load
6443        assert_eq!(loaded.list_scheduled().len(), 2);
6444        // First message should have affect
6445        let first = &loaded.temporal_queue[0];
6446        assert_eq!(first.sender, "alice");
6447        assert_eq!(first.content, "deliver later");
6448        assert!(first.affect.is_some());
6449        assert!(!first.delivered);
6450        // Second message
6451        let second = &loaded.temporal_queue[1];
6452        assert_eq!(second.sender, "bob");
6453        assert!(second.affect.is_none());
6454    }
6455
6456    // --- New ChannelState variant tests ---
6457
6458    #[test]
6459    fn channel_state_new_variants_display() {
6460        assert_eq!(ChannelState::Archived.to_string(), "archived");
6461        assert_eq!(ChannelState::SilentCommunion.to_string(), "silent_communion");
6462        assert_eq!(ChannelState::HiveMode.to_string(), "hive_mode");
6463        assert_eq!(ChannelState::PendingConsent.to_string(), "pending_consent");
6464    }
6465
6466    #[test]
6467    fn channel_state_archived_blocks_send_allows_receive() {
6468        let mut store = CommStore::new();
6469        let ch = store.create_channel("arch-ch", ChannelType::Group, None).unwrap();
6470        let ch_id = ch.id;
6471        store.join_channel(ch_id, "alice").unwrap();
6472        // Set state to Archived manually
6473        store.channels.get_mut(&ch_id).unwrap().state = ChannelState::Archived;
6474        // Send should fail
6475        assert!(store.send_message(ch_id, "alice", "hello", MessageType::Text).is_err());
6476    }
6477
6478    #[test]
6479    fn channel_state_pending_consent_blocks_both() {
6480        let mut store = CommStore::new();
6481        let ch = store.create_channel("pc-ch", ChannelType::Group, None).unwrap();
6482        let ch_id = ch.id;
6483        store.join_channel(ch_id, "alice").unwrap();
6484        store.channels.get_mut(&ch_id).unwrap().state = ChannelState::PendingConsent;
6485        assert!(store.send_message(ch_id, "alice", "hello", MessageType::Text).is_err());
6486    }
6487
6488    // --- CommTimestamp tests ---
6489
6490    #[test]
6491    fn comm_timestamp_increment_and_merge() {
6492        let mut ts = CommTimestamp::now("a");
6493        ts.increment("a");
6494        assert_eq!(ts.lamport, 1);
6495        assert_eq!(ts.vector_clock["a"], 1);
6496
6497        let mut ts2 = CommTimestamp::now("b");
6498        ts2.increment("b");
6499        ts2.increment("b");
6500
6501        ts.merge(&ts2, "a");
6502        assert_eq!(ts.lamport, 3); // max(1,2)+1
6503        assert_eq!(ts.vector_clock["a"], 2); // was 1, merge increments
6504        assert_eq!(ts.vector_clock["b"], 2);
6505    }
6506
6507    #[test]
6508    fn comm_timestamp_happens_before_basic() {
6509        let mut a = CommTimestamp::now("x");
6510        a.increment("x");
6511        let mut b = a.clone();
6512        b.increment("x");
6513        assert!(a.happens_before(&b));
6514        assert!(!b.happens_before(&a));
6515        // Equal clocks: not happens-before
6516        let c = a.clone();
6517        assert!(!a.happens_before(&c));
6518    }
6519
6520    // --- Audit log tests ---
6521
6522    #[test]
6523    fn audit_log_and_retrieve() {
6524        let mut store = CommStore::new();
6525        store.log_audit(AuditEventType::ChannelCreated, "agent-1", "Created general", Some("1".to_string()));
6526        store.log_audit(AuditEventType::MessageSent, "agent-2", "Sent hello", None);
6527        store.log_audit(AuditEventType::ConsentGranted, "agent-1", "Granted read to agent-2", Some("consent-1".to_string()));
6528
6529        let all = store.get_audit_log(None);
6530        assert_eq!(all.len(), 3);
6531
6532        let last2 = store.get_audit_log(Some(2));
6533        assert_eq!(last2.len(), 2);
6534        assert_eq!(last2[0].description, "Sent hello");
6535        assert_eq!(last2[1].description, "Granted read to agent-2");
6536    }
6537
6538    #[test]
6539    fn audit_log_in_stats() {
6540        let mut store = CommStore::new();
6541        store.log_audit(AuditEventType::AuthFailure, "attacker", "Bad credentials", None);
6542        store.log_audit(AuditEventType::RateLimitExceeded, "spammer", "Too many messages", None);
6543        let stats = store.stats();
6544        assert_eq!(stats.audit_log_count, 2);
6545    }
6546
6547    // --- RateLimitConfig tests ---
6548
6549    #[test]
6550    fn rate_limit_config_defaults_in_store() {
6551        let store = CommStore::new();
6552        assert_eq!(store.rate_limit_config.messages_per_minute, 60);
6553        assert_eq!(store.rate_limit_config.semantic_per_minute, 10);
6554        assert_eq!(store.rate_limit_config.affect_per_minute, 30);
6555        assert_eq!(store.rate_limit_config.hive_per_hour, 5);
6556        assert_eq!(store.rate_limit_config.federation_per_minute, 20);
6557    }
6558
6559    // -----------------------------------------------------------------------
6560    // Consent enforcement tests
6561    // -----------------------------------------------------------------------
6562
6563    #[test]
6564    fn consent_enforcement_blocks_semantic_without_consent() {
6565        let mut store = CommStore::new();
6566        let ch = store
6567            .create_channel("consent-test", ChannelType::Group, None)
6568            .unwrap();
6569        store.join_channel(ch.id, "sender-agent").unwrap();
6570        store.join_channel(ch.id, "receiver-agent").unwrap();
6571
6572        // Attempt to send affect-enriched content without consent
6573        let result = store.send_message(
6574            ch.id,
6575            "sender-agent",
6576            "[affect: joy intensity=0.8] Great news!",
6577            MessageType::Text,
6578        );
6579
6580        assert!(result.is_err(), "Affect-enriched message should be blocked without consent");
6581        match result.unwrap_err() {
6582            CommError::ConsentDenied { reason } => {
6583                assert!(
6584                    reason.contains("receiver-agent"),
6585                    "Error should mention the participant who hasn't granted consent"
6586                );
6587                assert!(
6588                    reason.contains("SendMessages"),
6589                    "Error should mention the required consent scope"
6590                );
6591            }
6592            other => panic!("Expected ConsentDenied, got: {:?}", other),
6593        }
6594    }
6595
6596    #[test]
6597    fn consent_enforcement_allows_with_grant() {
6598        let mut store = CommStore::new();
6599        let ch = store
6600            .create_channel("consent-allow-test", ChannelType::Group, None)
6601            .unwrap();
6602        store.join_channel(ch.id, "sender-agent").unwrap();
6603        store.join_channel(ch.id, "receiver-agent").unwrap();
6604
6605        // Grant SendMessages consent from receiver to sender
6606        store
6607            .grant_consent(
6608                "receiver-agent",
6609                "sender-agent",
6610                ConsentScope::SendMessages,
6611                Some("Allow affect messages".to_string()),
6612                None,
6613            )
6614            .unwrap();
6615
6616        // Now sending affect-enriched content should succeed
6617        let result = store.send_message(
6618            ch.id,
6619            "sender-agent",
6620            "[affect: joy intensity=0.8] Great news!",
6621            MessageType::Text,
6622        );
6623
6624        assert!(
6625            result.is_ok(),
6626            "Affect-enriched message should be allowed after consent grant"
6627        );
6628
6629        // Plain text should also work (no consent needed)
6630        let result2 = store.send_message(
6631            ch.id,
6632            "sender-agent",
6633            "Plain text message",
6634            MessageType::Text,
6635        );
6636        assert!(result2.is_ok(), "Plain text should always be allowed");
6637    }
6638
6639    // -----------------------------------------------------------------------
6640    // Rate limiting tests
6641    // -----------------------------------------------------------------------
6642
6643    #[test]
6644    fn rate_limiting_blocks_after_threshold() {
6645        let mut store = CommStore::new();
6646        // Set a very low rate limit for testing
6647        store.rate_limit_config.messages_per_minute = 3;
6648
6649        let ch = store
6650            .create_channel("rate-test", ChannelType::Group, None)
6651            .unwrap();
6652        store.join_channel(ch.id, "fast-sender").unwrap();
6653
6654        // Send messages up to the limit
6655        for i in 0..3 {
6656            let result = store.send_message(
6657                ch.id,
6658                "fast-sender",
6659                &format!("Message {}", i),
6660                MessageType::Text,
6661            );
6662            assert!(result.is_ok(), "Message {} should succeed (within limit)", i);
6663        }
6664
6665        // The 4th message should be rate-limited
6666        let result = store.send_message(
6667            ch.id,
6668            "fast-sender",
6669            "One too many",
6670            MessageType::Text,
6671        );
6672        assert!(result.is_err(), "4th message should be rate-limited");
6673        match result.unwrap_err() {
6674            CommError::RateLimitExceeded { limit } => {
6675                assert!(
6676                    limit.contains("fast-sender"),
6677                    "Error should mention the sender"
6678                );
6679                assert!(
6680                    limit.contains("3"),
6681                    "Error should mention the limit threshold"
6682                );
6683            }
6684            other => panic!("Expected RateLimitExceeded, got: {:?}", other),
6685        }
6686
6687        // A different sender should NOT be rate-limited
6688        store.join_channel(ch.id, "other-sender").unwrap();
6689        let result = store.send_message(
6690            ch.id,
6691            "other-sender",
6692            "I can still send",
6693            MessageType::Text,
6694        );
6695        assert!(
6696            result.is_ok(),
6697            "Different sender should have independent rate limit"
6698        );
6699    }
6700
6701    #[test]
6702    fn rate_limiting_resets_after_window() {
6703        let mut store = CommStore::new();
6704        store.rate_limit_config.messages_per_minute = 2;
6705
6706        let ch = store
6707            .create_channel("rate-reset-test", ChannelType::Group, None)
6708            .unwrap();
6709        store.join_channel(ch.id, "resetting-sender").unwrap();
6710
6711        // Send up to the limit
6712        store
6713            .send_message(ch.id, "resetting-sender", "msg1", MessageType::Text)
6714            .unwrap();
6715        store
6716            .send_message(ch.id, "resetting-sender", "msg2", MessageType::Text)
6717            .unwrap();
6718
6719        // Verify rate limit is hit
6720        let blocked = store.send_message(
6721            ch.id,
6722            "resetting-sender",
6723            "blocked",
6724            MessageType::Text,
6725        );
6726        assert!(blocked.is_err(), "Should be rate-limited");
6727
6728        // Simulate window reset by manipulating the tracker's last_minute_reset
6729        // to be more than 60 seconds ago.
6730        if let Some(tracker) = store.rate_trackers.get_mut("resetting-sender") {
6731            tracker.last_minute_reset = tracker.last_minute_reset.saturating_sub(61);
6732        }
6733
6734        // Now sending should succeed (window reset)
6735        let result = store.send_message(
6736            ch.id,
6737            "resetting-sender",
6738            "after reset",
6739            MessageType::Text,
6740        );
6741        assert!(
6742            result.is_ok(),
6743            "Message should succeed after rate limit window resets"
6744        );
6745    }
6746
6747    // -----------------------------------------------------------------------
6748    // Audit log tests
6749    // -----------------------------------------------------------------------
6750
6751    #[test]
6752    fn audit_log_records_operations() {
6753        let mut store = CommStore::new();
6754        assert!(store.audit_log.is_empty(), "Audit log should start empty");
6755
6756        // 1) create_channel should generate ChannelCreated audit
6757        let ch = store
6758            .create_channel("audit-test", ChannelType::Group, None)
6759            .unwrap();
6760        let channel_created_count = store
6761            .audit_log
6762            .iter()
6763            .filter(|e| e.event_type == AuditEventType::ChannelCreated)
6764            .count();
6765        assert_eq!(channel_created_count, 1, "Should have one ChannelCreated audit entry");
6766
6767        // 2) send_message should generate MessageSent audit
6768        store.join_channel(ch.id, "audit-agent").unwrap();
6769        store
6770            .send_message(ch.id, "audit-agent", "hello", MessageType::Text)
6771            .unwrap();
6772        let msg_sent_count = store
6773            .audit_log
6774            .iter()
6775            .filter(|e| e.event_type == AuditEventType::MessageSent)
6776            .count();
6777        assert_eq!(msg_sent_count, 1, "Should have one MessageSent audit entry");
6778
6779        // 3) grant_consent should generate ConsentGranted audit
6780        store
6781            .grant_consent(
6782                "grantor-a",
6783                "grantee-b",
6784                ConsentScope::ReadMessages,
6785                None,
6786                None,
6787            )
6788            .unwrap();
6789        let consent_granted_count = store
6790            .audit_log
6791            .iter()
6792            .filter(|e| e.event_type == AuditEventType::ConsentGranted)
6793            .count();
6794        assert_eq!(consent_granted_count, 1, "Should have one ConsentGranted audit entry");
6795
6796        // 4) revoke_consent should generate ConsentRevoked audit
6797        let _ = store.revoke_consent("grantor-a", "grantee-b", &ConsentScope::ReadMessages);
6798        let consent_revoked_count = store
6799            .audit_log
6800            .iter()
6801            .filter(|e| e.event_type == AuditEventType::ConsentRevoked)
6802            .count();
6803        assert_eq!(consent_revoked_count, 1, "Should have one ConsentRevoked audit entry");
6804
6805        // 5) close_channel should generate ChannelClosed audit
6806        store.close_channel(ch.id).unwrap();
6807        let channel_closed_count = store
6808            .audit_log
6809            .iter()
6810            .filter(|e| e.event_type == AuditEventType::ChannelClosed)
6811            .count();
6812        assert_eq!(channel_closed_count, 1, "Should have one ChannelClosed audit entry");
6813
6814        // Verify total audit entries accumulated
6815        assert!(
6816            store.audit_log.len() >= 5,
6817            "Audit log should have at least 5 entries, got {}",
6818            store.audit_log.len()
6819        );
6820    }
6821
6822    // -----------------------------------------------------------------------
6823    // Signature verification tests
6824    // -----------------------------------------------------------------------
6825
6826    #[test]
6827    fn signature_verification_detects_tampering() {
6828        let mut store = CommStore::new();
6829        let ch = store
6830            .create_channel("sig-test", ChannelType::Group, None)
6831            .unwrap();
6832        store.join_channel(ch.id, "agent-sig").unwrap();
6833
6834        let msg = store
6835            .send_message(ch.id, "agent-sig", "Original content", MessageType::Text)
6836            .unwrap();
6837        let msg_id = msg.id;
6838
6839        // Verify the untampered message passes signature check
6840        assert!(
6841            store.verify_message_signature(msg_id),
6842            "Untampered message should pass signature verification"
6843        );
6844
6845        // Tamper with the message content directly
6846        if let Some(message) = store.messages.get_mut(&msg_id) {
6847            message.content = "Tampered content".to_string();
6848        }
6849
6850        // Now signature verification should fail
6851        let audit_len_before = store.audit_log.len();
6852        let result = store.verify_message_signature(msg_id);
6853        assert!(
6854            !result,
6855            "Tampered message should fail signature verification"
6856        );
6857
6858        // Verify a SignatureWarning audit entry was logged
6859        let sig_warnings = store
6860            .audit_log
6861            .iter()
6862            .skip(audit_len_before)
6863            .filter(|e| e.event_type == AuditEventType::SignatureWarning)
6864            .count();
6865        assert_eq!(
6866            sig_warnings, 1,
6867            "Should have logged a SignatureWarning audit entry for the tampered message"
6868        );
6869    }
6870
6871    // -- File locking tests --
6872
6873    #[test]
6874    fn file_locking_exclusive_blocks_second_try() {
6875        let dir = tempfile::tempdir().unwrap();
6876        let data_path = dir.path().join("test.acomm");
6877
6878        // Acquire an exclusive lock.
6879        let lock1 = CommFileLock::acquire(&data_path).unwrap();
6880
6881        // A non-blocking try_acquire on the same path should fail.
6882        let result = CommFileLock::try_acquire(&data_path);
6883        assert!(
6884            result.is_err(),
6885            "try_acquire should fail while exclusive lock is held"
6886        );
6887
6888        // Clean up.
6889        lock1.release().unwrap();
6890    }
6891
6892    #[test]
6893    fn file_locking_release_allows_reacquire() {
6894        let dir = tempfile::tempdir().unwrap();
6895        let data_path = dir.path().join("test.acomm");
6896
6897        // Acquire, then release.
6898        let lock1 = CommFileLock::acquire(&data_path).unwrap();
6899        lock1.release().unwrap();
6900
6901        // Now acquiring again should succeed.
6902        let lock2 = CommFileLock::acquire(&data_path).unwrap();
6903        lock2.release().unwrap();
6904    }
6905
6906    #[test]
6907    fn file_locking_stale_recovery() {
6908        let dir = tempfile::tempdir().unwrap();
6909        let data_path = dir.path().join("test.acomm");
6910        let lock_path = data_path.with_extension("acomm.lock");
6911
6912        // Create a fake stale lock file.
6913        std::fs::File::create(&lock_path).unwrap();
6914
6915        // Backdate the lock file's mtime to 120 seconds in the past.
6916        let old_time = FileTime::from_unix_time(
6917            std::time::SystemTime::now()
6918                .duration_since(std::time::UNIX_EPOCH)
6919                .unwrap()
6920                .as_secs() as i64
6921                - 120,
6922            0,
6923        );
6924        filetime::set_file_mtime(&lock_path, old_time).unwrap();
6925
6926        // A max_age of 60 s means anything older than 60 s is stale.
6927        let recovered = CommFileLock::recover_stale(&data_path, 60).unwrap();
6928        assert!(recovered, "Should have recovered stale lock file");
6929        assert!(
6930            !lock_path.exists(),
6931            "Stale lock file should have been removed"
6932        );
6933    }
6934
6935    #[test]
6936    fn file_locking_stale_recovery_not_stale() {
6937        let dir = tempfile::tempdir().unwrap();
6938        let data_path = dir.path().join("test.acomm");
6939        let lock_path = data_path.with_extension("acomm.lock");
6940
6941        // Create a fresh lock file.
6942        std::fs::File::create(&lock_path).unwrap();
6943
6944        // With a large max_age, the lock should not be considered stale.
6945        let recovered = CommFileLock::recover_stale(&data_path, 3600).unwrap();
6946        assert!(!recovered, "Fresh lock should not be recovered");
6947        assert!(lock_path.exists(), "Fresh lock file should still exist");
6948    }
6949
6950    #[test]
6951    fn file_locking_no_lock_file_recovery() {
6952        let dir = tempfile::tempdir().unwrap();
6953        let data_path = dir.path().join("test.acomm");
6954
6955        // No lock file exists — recovery should return false.
6956        let recovered = CommFileLock::recover_stale(&data_path, 0).unwrap();
6957        assert!(!recovered, "No lock file means nothing to recover");
6958    }
6959
6960    #[test]
6961    fn file_locking_save_load_with_locks() {
6962        let dir = tempfile::tempdir().unwrap();
6963        let path = dir.path().join("locked.acomm");
6964
6965        // Build a store with some data.
6966        let mut store = CommStore::new();
6967        store
6968            .create_channel("lock-test", ChannelType::Group, None)
6969            .unwrap();
6970        store
6971            .send_message(1, "agent-a", "hello from lock test", MessageType::Text)
6972            .unwrap();
6973
6974        // Save (internally acquires exclusive lock).
6975        store.save(&path).unwrap();
6976
6977        // Load (internally acquires shared lock).
6978        let loaded = CommStore::load(&path).unwrap();
6979        assert_eq!(loaded.channels.len(), 1);
6980        assert_eq!(loaded.messages.len(), 1);
6981
6982        let msg = loaded.messages.values().next().unwrap();
6983        assert_eq!(msg.content, "hello from lock test");
6984    }
6985
6986    #[test]
6987    fn file_locking_shared_allows_multiple_readers() {
6988        let dir = tempfile::tempdir().unwrap();
6989        let data_path = dir.path().join("test.acomm");
6990
6991        // Multiple shared locks should coexist.
6992        let lock1 = CommFileLock::acquire_shared(&data_path).unwrap();
6993        let lock2 = CommFileLock::acquire_shared(&data_path).unwrap();
6994
6995        // Both held simultaneously — no panic, no error.
6996        lock1.release().unwrap();
6997        lock2.release().unwrap();
6998    }
6999
7000    #[test]
7001    fn file_locking_drop_releases_lock() {
7002        let dir = tempfile::tempdir().unwrap();
7003        let data_path = dir.path().join("test.acomm");
7004
7005        // Acquire and then drop without explicit release.
7006        {
7007            let _lock = CommFileLock::acquire(&data_path).unwrap();
7008            // _lock dropped here.
7009        }
7010
7011        // Should be able to re-acquire after drop.
7012        let lock2 = CommFileLock::try_acquire(&data_path).unwrap();
7013        lock2.release().unwrap();
7014    }
7015
7016
7017    // -- Affect Contagion / Echo Chain / Summarization tests --
7018
7019    #[test]
7020    fn affect_contagion_basic() {
7021        let mut store = CommStore::new();
7022        let ch = store
7023            .create_channel("affect-test", ChannelType::Group, None)
7024            .unwrap();
7025        store.join_channel(ch.id, "alice").unwrap();
7026        store.join_channel(ch.id, "bob").unwrap();
7027
7028        // Send a message with affect metadata
7029        let msg = store
7030            .send_message(ch.id, "alice", "I am so happy!", MessageType::Text)
7031            .unwrap();
7032        // Manually set affect metadata on the message
7033        store
7034            .messages
7035            .get_mut(&msg.id)
7036            .unwrap()
7037            .metadata
7038            .insert("valence".to_string(), "0.9".to_string());
7039        store
7040            .messages
7041            .get_mut(&msg.id)
7042            .unwrap()
7043            .metadata
7044            .insert("arousal".to_string(), "0.7".to_string());
7045        store
7046            .messages
7047            .get_mut(&msg.id)
7048            .unwrap()
7049            .metadata
7050            .insert("dominance".to_string(), "0.6".to_string());
7051
7052        store.set_affect_resistance(0.0);
7053        let results = store.process_affect_contagion(ch.id);
7054
7055        // Bob should be affected
7056        assert!(!results.is_empty());
7057        let bob_result = results.iter().find(|(name, _, _, _)| name == "bob");
7058        assert!(bob_result.is_some());
7059        let (_, valence, arousal, _) = bob_result.unwrap();
7060        assert!(*valence > 0.0, "Bob's valence should be positive");
7061        assert!(*arousal > 0.0, "Bob's arousal should be positive");
7062    }
7063
7064    #[test]
7065    fn affect_contagion_empty_channel() {
7066        let mut store = CommStore::new();
7067        // Non-existent channel
7068        let results = store.process_affect_contagion(999);
7069        assert!(results.is_empty());
7070    }
7071
7072    #[test]
7073    fn affect_history_empty() {
7074        let store = CommStore::new();
7075        let history = store.get_affect_history("nonexistent-agent");
7076        assert_eq!(history.agent, "nonexistent-agent");
7077        assert!(history.states.is_empty());
7078    }
7079
7080    #[test]
7081    fn affect_history_with_state() {
7082        let mut store = CommStore::new();
7083        store.affect_states.insert(
7084            "agent-x".to_string(),
7085            AffectState {
7086                valence: 0.5,
7087                arousal: 0.3,
7088                dominance: 0.7,
7089                ..AffectState::default()
7090            },
7091        );
7092        let history = store.get_affect_history("agent-x");
7093        assert_eq!(history.agent, "agent-x");
7094        assert!(!history.states.is_empty());
7095        let last = history.states.last().unwrap();
7096        assert_eq!(last.source, "current");
7097        assert!((last.valence - 0.5).abs() < 0.01);
7098    }
7099
7100    #[test]
7101    fn affect_decay_reduces_state() {
7102        let mut store = CommStore::new();
7103        store.affect_states.insert(
7104            "agent-y".to_string(),
7105            AffectState {
7106                valence: 0.8,
7107                arousal: 0.6,
7108                dominance: 0.5,
7109                ..AffectState::default()
7110            },
7111        );
7112        store.apply_affect_decay(0.5);
7113        let state = store.affect_states.get("agent-y").unwrap();
7114        assert!((state.valence - 0.4).abs() < 0.01, "valence should halve");
7115        assert!((state.arousal - 0.3).abs() < 0.01, "arousal should halve");
7116    }
7117
7118    #[test]
7119    fn forward_message_basic() {
7120        let mut store = CommStore::new();
7121        let ch1 = store
7122            .create_channel("source-chan", ChannelType::Group, None)
7123            .unwrap();
7124        let ch2 = store
7125            .create_channel("target-chan", ChannelType::Group, None)
7126            .unwrap();
7127        store.join_channel(ch1.id, "alice").unwrap();
7128        store.join_channel(ch2.id, "bob").unwrap();
7129
7130        let msg = store
7131            .send_message(ch1.id, "alice", "Hello world", MessageType::Text)
7132            .unwrap();
7133
7134        let fwd_id = store
7135            .forward_message(msg.id, ch2.id, "bob")
7136            .unwrap();
7137
7138        let fwd_msg = store.messages.get(&fwd_id).unwrap();
7139        assert!(fwd_msg.content.starts_with("[Forwarded]"));
7140        assert_eq!(fwd_msg.channel_id, ch2.id);
7141        assert_eq!(
7142            fwd_msg.metadata.get("forwarded_from").unwrap(),
7143            &msg.id.to_string()
7144        );
7145        assert_eq!(fwd_msg.metadata.get("echo_depth").unwrap(), "1");
7146    }
7147
7148    #[test]
7149    fn forward_message_not_found() {
7150        let mut store = CommStore::new();
7151        let ch = store
7152            .create_channel("target", ChannelType::Group, None)
7153            .unwrap();
7154        let result = store.forward_message(999, ch.id, "bob");
7155        assert!(result.is_err());
7156        assert!(result.unwrap_err().contains("not found"));
7157    }
7158
7159    #[test]
7160    fn forward_message_target_not_found() {
7161        let mut store = CommStore::new();
7162        let ch = store
7163            .create_channel("source", ChannelType::Group, None)
7164            .unwrap();
7165        store.join_channel(ch.id, "alice").unwrap();
7166        let msg = store
7167            .send_message(ch.id, "alice", "test", MessageType::Text)
7168            .unwrap();
7169        let result = store.forward_message(msg.id, 999, "bob");
7170        assert!(result.is_err());
7171        assert!(result.unwrap_err().contains("not found"));
7172    }
7173
7174    #[test]
7175    fn echo_chain_and_depth() {
7176        let mut store = CommStore::new();
7177        let ch1 = store
7178            .create_channel("ch1", ChannelType::Group, None)
7179            .unwrap();
7180        let ch2 = store
7181            .create_channel("ch2", ChannelType::Group, None)
7182            .unwrap();
7183        let ch3 = store
7184            .create_channel("ch3", ChannelType::Group, None)
7185            .unwrap();
7186        store.join_channel(ch1.id, "alice").unwrap();
7187        store.join_channel(ch2.id, "bob").unwrap();
7188        store.join_channel(ch3.id, "charlie").unwrap();
7189
7190        let orig = store
7191            .send_message(ch1.id, "alice", "Original", MessageType::Text)
7192            .unwrap();
7193        assert_eq!(store.get_echo_depth(orig.id), 0);
7194
7195        let fwd1 = store.forward_message(orig.id, ch2.id, "bob").unwrap();
7196        assert_eq!(store.get_echo_depth(fwd1), 1);
7197
7198        let fwd2 = store.forward_message(fwd1, ch3.id, "charlie").unwrap();
7199        assert_eq!(store.get_echo_depth(fwd2), 2);
7200
7201        let chain = store.query_echo_chain(fwd2);
7202        assert!(chain.len() >= 3);
7203        assert_eq!(chain[0].message_id, orig.id);
7204        assert_eq!(chain[0].depth, 0);
7205    }
7206
7207    #[test]
7208    fn summarize_conversation_basic() {
7209        let mut store = CommStore::new();
7210        let ch = store
7211            .create_channel("summary-test", ChannelType::Group, None)
7212            .unwrap();
7213        store.join_channel(ch.id, "alice").unwrap();
7214        store.join_channel(ch.id, "bob").unwrap();
7215
7216        store
7217            .send_message(ch.id, "alice", "Hello!", MessageType::Text)
7218            .unwrap();
7219        store
7220            .send_message(ch.id, "bob", "Hi there!", MessageType::Text)
7221            .unwrap();
7222        store
7223            .send_message(ch.id, "alice", "How are you?", MessageType::Text)
7224            .unwrap();
7225
7226        let summary = store.summarize_conversation(ch.id).unwrap();
7227        assert_eq!(summary.channel_id, ch.id);
7228        assert_eq!(summary.channel_name, "summary-test");
7229        assert_eq!(summary.message_count, 3);
7230        assert_eq!(summary.participant_count, 2);
7231        assert!(summary.avg_message_length > 0.0);
7232        assert!(!summary.has_affect_data);
7233    }
7234
7235    #[test]
7236    fn summarize_conversation_not_found() {
7237        let store = CommStore::new();
7238        let result = store.summarize_conversation(999);
7239        assert!(result.is_err());
7240    }
7241
7242    // -----------------------------------------------------------------------
7243    // CommId and Rich Content integration tests
7244    // -----------------------------------------------------------------------
7245
7246    #[test]
7247    fn test_assign_comm_ids_fills_missing() {
7248        let mut store = CommStore::new();
7249        let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7250        store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7251        store.send_message(ch_id, "bob", "world", MessageType::Text).unwrap();
7252
7253        // Before assign, all comm_ids should be None
7254        for msg in store.messages.values() {
7255            assert!(msg.comm_id.is_none());
7256        }
7257        for chan in store.channels.values() {
7258            assert!(chan.comm_id.is_none());
7259        }
7260
7261        store.assign_comm_ids();
7262
7263        // After assign, all should be Some
7264        for msg in store.messages.values() {
7265            assert!(msg.comm_id.is_some());
7266        }
7267        for chan in store.channels.values() {
7268            assert!(chan.comm_id.is_some());
7269        }
7270    }
7271
7272    #[test]
7273    fn test_assign_comm_ids_idempotent() {
7274        let mut store = CommStore::new();
7275        let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7276        store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7277
7278        store.assign_comm_ids();
7279        let first_id = store.messages.values().next().unwrap().comm_id;
7280
7281        store.assign_comm_ids();
7282        let second_id = store.messages.values().next().unwrap().comm_id;
7283
7284        assert_eq!(first_id, second_id, "assign_comm_ids should be idempotent");
7285    }
7286
7287    #[test]
7288    fn test_get_message_by_comm_id() {
7289        let mut store = CommStore::new();
7290        let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7291        let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text).unwrap();
7292
7293        store.assign_comm_ids();
7294
7295        let comm_id = store.messages.get(&msg.id).unwrap().comm_id.unwrap();
7296        let found = store.get_message_by_comm_id(&comm_id);
7297        assert!(found.is_some());
7298        assert_eq!(found.unwrap().id, msg.id);
7299    }
7300
7301    #[test]
7302    fn test_get_channel_by_comm_id() {
7303        let mut store = CommStore::new();
7304        let ch = store.create_channel("test", ChannelType::Group, None).unwrap().clone();
7305
7306        store.assign_comm_ids();
7307
7308        let comm_id = store.channels.get(&ch.id).unwrap().comm_id.unwrap();
7309        let found = store.get_channel_by_comm_id(&comm_id);
7310        assert!(found.is_some());
7311        assert_eq!(found.unwrap().id, ch.id);
7312    }
7313
7314    #[test]
7315    fn test_send_rich_message() {
7316        let mut store = CommStore::new();
7317        let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7318        store.join_channel(ch_id, "alice").unwrap();
7319
7320        let content = MessageContent::Semantic(SemanticContent {
7321            text: "The weather is nice".into(),
7322            fragments: vec!["weather".into(), "nice".into()],
7323            context: Some("small talk".into()),
7324            perspective: None,
7325        });
7326
7327        let msg = store.send_rich_message(ch_id, "alice", content, MessageType::Text).unwrap();
7328        assert!(msg.rich_content_json.is_some());
7329        assert_eq!(msg.content, "The weather is nice");
7330
7331        // Parse it back
7332        let rich = store.get_rich_content(msg.id).unwrap();
7333        assert!(rich.is_some());
7334        let rc = rich.unwrap();
7335        assert!(rc.is_rich());
7336        assert_eq!(rc.as_text(), "The weather is nice");
7337    }
7338
7339    #[test]
7340    fn test_get_rich_content_none_for_plain_message() {
7341        let mut store = CommStore::new();
7342        let ch_id = store.create_channel("test", ChannelType::Group, None).unwrap().id;
7343        let msg = store.send_message(ch_id, "alice", "plain", MessageType::Text).unwrap();
7344
7345        let rich = store.get_rich_content(msg.id).unwrap();
7346        assert!(rich.is_none());
7347    }
7348
7349    #[test]
7350    fn test_get_rich_content_message_not_found() {
7351        let store = CommStore::new();
7352        let result = store.get_rich_content(9999);
7353        assert!(result.is_err());
7354    }
7355
7356    #[test]
7357    fn test_message_backward_compat_without_new_fields() {
7358        // Simulate deserializing an old message without the new fields
7359        let json = r#"{
7360            "id": 1,
7361            "channel_id": 1,
7362            "sender": "alice",
7363            "recipient": null,
7364            "content": "hello",
7365            "message_type": "Text",
7366            "timestamp": "2026-01-01T00:00:00Z",
7367            "metadata": {},
7368            "signature": null,
7369            "acknowledged_by": [],
7370            "status": "Sent",
7371            "priority": "Normal",
7372            "reply_to": null,
7373            "correlation_id": null,
7374            "thread_id": null,
7375            "comm_timestamp": {"wall_clock": "2026-01-01T00:00:00Z", "lamport": 0, "agent_id": "alice", "vector_clock": {}}
7376        }"#;
7377        let msg: Message = serde_json::from_str(json).unwrap();
7378        assert!(msg.rich_content_json.is_none());
7379        assert!(msg.comm_id.is_none());
7380        assert_eq!(msg.content, "hello");
7381    }
7382
7383    // =====================================================================
7384    // Trust enforcement tests
7385    // =====================================================================
7386
7387    #[test]
7388    fn test_trust_enforcement_send_message_blocked() {
7389        let mut store = CommStore::new();
7390        let ch = store
7391            .create_channel("secure", ChannelType::Group, None)
7392            .unwrap();
7393        let ch_id = ch.id;
7394
7395        // Set channel to require High trust
7396        let mut config = store.get_channel(ch_id).unwrap().config.clone();
7397        config.min_trust_level = Some(CommTrustLevel::High);
7398        store.set_channel_config(ch_id, config).unwrap();
7399
7400        // Set alice's trust level to Basic (below High)
7401        store.set_trust_level("alice", CommTrustLevel::Basic).unwrap();
7402        store.join_channel(ch_id, "alice").unwrap_err(); // also blocked at join
7403
7404        // Set to High so she can join, then lower and try to send
7405        store.set_trust_level("alice", CommTrustLevel::High).unwrap();
7406        store.join_channel(ch_id, "alice").unwrap();
7407
7408        // Lower trust and try to send
7409        store.set_trust_level("alice", CommTrustLevel::Basic).unwrap();
7410        let result = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7411        assert!(result.is_err());
7412        let err = result.unwrap_err();
7413        assert!(
7414            err.to_string().contains("Trust level insufficient"),
7415            "Expected trust error, got: {}",
7416            err
7417        );
7418    }
7419
7420    #[test]
7421    fn test_trust_enforcement_send_message_allowed() {
7422        let mut store = CommStore::new();
7423        let ch = store
7424            .create_channel("secure", ChannelType::Group, None)
7425            .unwrap();
7426        let ch_id = ch.id;
7427
7428        // Set channel to require Basic trust
7429        let mut config = store.get_channel(ch_id).unwrap().config.clone();
7430        config.min_trust_level = Some(CommTrustLevel::Basic);
7431        store.set_channel_config(ch_id, config).unwrap();
7432
7433        // Set alice's trust to Standard (above Basic)
7434        store.set_trust_level("alice", CommTrustLevel::Standard).unwrap();
7435        store.join_channel(ch_id, "alice").unwrap();
7436        let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7437        assert!(msg.is_ok());
7438    }
7439
7440    #[test]
7441    fn test_trust_enforcement_no_min_trust_allows_all() {
7442        let mut store = CommStore::new();
7443        let ch = store
7444            .create_channel("open", ChannelType::Group, None)
7445            .unwrap();
7446        let ch_id = ch.id;
7447        // No min_trust_level set (None) — anyone can send
7448        store.set_trust_level("alice", CommTrustLevel::None).unwrap();
7449        store.join_channel(ch_id, "alice").unwrap();
7450        let msg = store.send_message(ch_id, "alice", "hello", MessageType::Text);
7451        assert!(msg.is_ok());
7452    }
7453
7454    #[test]
7455    fn test_trust_enforcement_broadcast_blocked() {
7456        let mut store = CommStore::new();
7457        let ch = store
7458            .create_channel("secure-bc", ChannelType::Broadcast, None)
7459            .unwrap();
7460        let ch_id = ch.id;
7461
7462        // Require Full trust
7463        let mut config = store.get_channel(ch_id).unwrap().config.clone();
7464        config.min_trust_level = Some(CommTrustLevel::Full);
7465        store.set_channel_config(ch_id, config).unwrap();
7466
7467        // Alice has Standard trust — too low
7468        store.set_trust_level("alice", CommTrustLevel::Standard).unwrap();
7469        let result = store.broadcast(ch_id, "alice", "hello everyone");
7470        assert!(result.is_err());
7471        assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7472    }
7473
7474    #[test]
7475    fn test_trust_enforcement_join_channel_blocked() {
7476        let mut store = CommStore::new();
7477        let ch = store
7478            .create_channel("exclusive", ChannelType::Group, None)
7479            .unwrap();
7480        let ch_id = ch.id;
7481
7482        // Require High trust to join
7483        let mut config = store.get_channel(ch_id).unwrap().config.clone();
7484        config.min_trust_level = Some(CommTrustLevel::High);
7485        store.set_channel_config(ch_id, config).unwrap();
7486
7487        store.set_trust_level("bob", CommTrustLevel::Basic).unwrap();
7488        let result = store.join_channel(ch_id, "bob");
7489        assert!(result.is_err());
7490        assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7491    }
7492
7493    #[test]
7494    fn test_trust_enforcement_publish_blocked() {
7495        let mut store = CommStore::new();
7496        // Pre-create a pubsub channel with trust requirement
7497        let ch = store
7498            .create_channel("news-feed", ChannelType::PubSub, None)
7499            .unwrap();
7500        let ch_id = ch.id;
7501
7502        let mut config = store.get_channel(ch_id).unwrap().config.clone();
7503        config.min_trust_level = Some(CommTrustLevel::High);
7504        store.set_channel_config(ch_id, config).unwrap();
7505
7506        store.set_trust_level("low-trust-pub", CommTrustLevel::Minimal).unwrap();
7507
7508        // Subscribe someone
7509        store.subscribe("news-feed", "subscriber1").unwrap();
7510
7511        let result = store.publish("news-feed", "low-trust-pub", "breaking news");
7512        assert!(result.is_err());
7513        assert!(result.unwrap_err().to_string().contains("Trust level insufficient"));
7514    }
7515
7516    // =====================================================================
7517    // Consent enforcement expansion tests
7518    // =====================================================================
7519
7520    #[test]
7521    fn test_consent_join_channel_open_by_default() {
7522        // When no JoinChannels consent gates exist, joining should succeed
7523        let (mut store, ch_id) = new_store_with_channel();
7524        let result = store.join_channel(ch_id, "alice");
7525        assert!(result.is_ok());
7526    }
7527
7528    #[test]
7529    fn test_consent_join_channel_blocked() {
7530        let (mut store, ch_id) = new_store_with_channel();
7531        // Create a JoinChannels consent gate that grants to bob but not alice
7532        store.grant_consent(
7533            "system", "bob", ConsentScope::JoinChannels,
7534            Some("approved".to_string()), None,
7535        ).unwrap();
7536
7537        // Now JoinChannels scope has at least one gate, so alice (no grant) is blocked
7538        let result = store.join_channel(ch_id, "alice");
7539        assert!(result.is_err());
7540        assert!(result.unwrap_err().to_string().contains("Consent"));
7541
7542        // bob should be allowed
7543        let result = store.join_channel(ch_id, "bob");
7544        assert!(result.is_ok());
7545    }
7546
7547    #[test]
7548    fn test_consent_schedule_message_open_by_default() {
7549        let mut store = CommStore::new();
7550        let ch = store
7551            .create_channel("temporal-ch", ChannelType::Temporal, None)
7552            .unwrap();
7553        // No ScheduleMessages consent gates — should work
7554        let result = store.schedule_message(
7555            ch.id, "alice", "future msg", TemporalTarget::Immediate, None,
7556        );
7557        assert!(result.is_ok());
7558    }
7559
7560    #[test]
7561    fn test_consent_schedule_message_blocked() {
7562        let mut store = CommStore::new();
7563        let ch = store
7564            .create_channel("temporal-ch", ChannelType::Temporal, None)
7565            .unwrap();
7566        // Create a ScheduleMessages gate for bob (alice has no grant)
7567        store.grant_consent(
7568            "system", "bob", ConsentScope::ScheduleMessages,
7569            Some("allowed".to_string()), None,
7570        ).unwrap();
7571
7572        let result = store.schedule_message(
7573            ch.id, "alice", "blocked msg", TemporalTarget::Immediate, None,
7574        );
7575        assert!(result.is_err());
7576        assert!(result.unwrap_err().to_string().contains("Consent"));
7577    }
7578
7579    #[test]
7580    fn test_consent_form_hive_open_by_default() {
7581        let mut store = CommStore::new();
7582        let result = store.form_hive(
7583            "test-hive", "coordinator",
7584            CollectiveDecisionMode::Consensus,
7585        );
7586        assert!(result.is_ok());
7587    }
7588
7589    #[test]
7590    fn test_consent_form_hive_blocked() {
7591        let mut store = CommStore::new();
7592        // Create a HiveParticipation gate for someone else
7593        store.grant_consent(
7594            "system", "other-agent", ConsentScope::HiveParticipation,
7595            Some("approved".to_string()), None,
7596        ).unwrap();
7597
7598        // coordinator has no grant, so should be blocked
7599        let result = store.form_hive(
7600            "test-hive", "coordinator",
7601            CollectiveDecisionMode::Consensus,
7602        );
7603        assert!(result.is_err());
7604        assert!(result.unwrap_err().to_string().contains("Consent"));
7605    }
7606
7607    #[test]
7608    fn test_consent_join_hive_blocked() {
7609        let mut store = CommStore::new();
7610        // Grant coordinator consent so they can form
7611        store.grant_consent(
7612            "system", "coordinator", ConsentScope::HiveParticipation,
7613            Some("approved".to_string()), None,
7614        ).unwrap();
7615        let hive = store.form_hive(
7616            "test-hive", "coordinator",
7617            CollectiveDecisionMode::Consensus,
7618        ).unwrap();
7619        let hive_id = hive.id;
7620
7621        // joiner has no HiveParticipation grant
7622        let result = store.join_hive(hive_id, "joiner", HiveRole::Member);
7623        assert!(result.is_err());
7624        assert!(result.unwrap_err().to_string().contains("Consent"));
7625
7626        // Grant joiner consent
7627        store.grant_consent(
7628            "system", "joiner", ConsentScope::HiveParticipation,
7629            Some("approved".to_string()), None,
7630        ).unwrap();
7631        let result = store.join_hive(hive_id, "joiner", HiveRole::Member);
7632        assert!(result.is_ok());
7633    }
7634
7635    #[test]
7636    fn test_consent_configure_federation_open_by_default() {
7637        let mut store = CommStore::new();
7638        let result = store.configure_federation(
7639            true, "zone-a", FederationPolicy::Allow,
7640        );
7641        assert!(result.is_ok());
7642    }
7643
7644    #[test]
7645    fn test_consent_configure_federation_blocked() {
7646        let mut store = CommStore::new();
7647        // Create a Federate consent gate for another agent
7648        store.grant_consent(
7649            "admin", "other-system", ConsentScope::Federate,
7650            Some("allowed".to_string()), None,
7651        ).unwrap();
7652
7653        // "system" has no Federate grant
7654        let result = store.configure_federation(
7655            true, "zone-a", FederationPolicy::Allow,
7656        );
7657        assert!(result.is_err());
7658        assert!(result.unwrap_err().to_string().contains("Consent"));
7659    }
7660
7661    // =====================================================================
7662    // Vector clock tests
7663    // =====================================================================
7664
7665    #[test]
7666    fn test_vector_clock_increment() {
7667        let mut ts = CommTimestamp::now("agent-a");
7668        assert_eq!(ts.lamport, 0);
7669        assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 0);
7670
7671        ts.increment("agent-a");
7672        assert_eq!(ts.lamport, 1);
7673        assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 1);
7674
7675        ts.increment("agent-a");
7676        assert_eq!(ts.lamport, 2);
7677        assert_eq!(*ts.vector_clock.get("agent-a").unwrap(), 2);
7678    }
7679
7680    #[test]
7681    fn test_vector_clock_merge() {
7682        let mut ts_a = CommTimestamp::now("agent-a");
7683        ts_a.increment("agent-a"); // lamport=1, vc={a:1}
7684        ts_a.increment("agent-a"); // lamport=2, vc={a:2}
7685
7686        let mut ts_b = CommTimestamp::now("agent-b");
7687        ts_b.increment("agent-b"); // lamport=1, vc={b:1}
7688
7689        // Merge b into a
7690        ts_a.merge(&ts_b, "agent-a");
7691        // lamport should be max(2,1)+1 = 3
7692        assert_eq!(ts_a.lamport, 3);
7693        // vector_clock should have a:3 (incremented on merge), b:1
7694        assert_eq!(*ts_a.vector_clock.get("agent-a").unwrap(), 3);
7695        assert_eq!(*ts_a.vector_clock.get("agent-b").unwrap(), 1);
7696    }
7697
7698    #[test]
7699    fn test_vector_clock_happens_before() {
7700        let mut ts_a = CommTimestamp::now("agent-a");
7701        ts_a.increment("agent-a"); // vc={a:1}
7702
7703        let mut ts_b = ts_a.clone();
7704        ts_b.increment("agent-a"); // vc={a:2}
7705
7706        // ts_a should happen-before ts_b
7707        assert!(ts_a.happens_before(&ts_b));
7708        // ts_b should NOT happen-before ts_a
7709        assert!(!ts_b.happens_before(&ts_a));
7710    }
7711
7712    #[test]
7713    fn test_vector_clock_concurrent() {
7714        let mut ts_a = CommTimestamp::now("agent-a");
7715        ts_a.increment("agent-a"); // vc={a:1}
7716
7717        let mut ts_b = CommTimestamp::now("agent-b");
7718        ts_b.increment("agent-b"); // vc={b:1}
7719
7720        // Neither happens before the other (concurrent)
7721        assert!(!ts_a.happens_before(&ts_b));
7722        assert!(!ts_b.happens_before(&ts_a));
7723    }
7724
7725    #[test]
7726    fn test_vector_clock_populated_in_send_message() {
7727        let (mut store, ch_id) = new_store_with_channel();
7728        store.join_channel(ch_id, "alice").unwrap();
7729
7730        let msg = store
7731            .send_message(ch_id, "alice", "hello", MessageType::Text)
7732            .unwrap();
7733
7734        // The vector clock should have alice's entry set to the lamport counter
7735        assert!(msg.comm_timestamp.lamport > 0);
7736        assert_eq!(
7737            *msg.comm_timestamp.vector_clock.get("alice").unwrap(),
7738            msg.comm_timestamp.lamport,
7739        );
7740    }
7741
7742    #[test]
7743    fn test_vector_clock_populated_in_broadcast() {
7744        let mut store = CommStore::new();
7745        let ch = store
7746            .create_channel("bc-chan", ChannelType::Broadcast, None)
7747            .unwrap();
7748        let ch_id = ch.id;
7749        store.join_channel(ch_id, "alice").unwrap();
7750        store.join_channel(ch_id, "bob").unwrap();
7751
7752        let msgs = store.broadcast(ch_id, "alice", "broadcast msg").unwrap();
7753        assert!(!msgs.is_empty());
7754        for m in &msgs {
7755            assert!(m.comm_timestamp.lamport > 0);
7756            assert_eq!(
7757                *m.comm_timestamp.vector_clock.get("alice").unwrap(),
7758                m.comm_timestamp.lamport,
7759            );
7760        }
7761    }
7762
7763    #[test]
7764    fn test_receive_messages_merges_lamport() {
7765        let (mut store, ch_id) = new_store_with_channel();
7766        store.join_channel(ch_id, "alice").unwrap();
7767        store.join_channel(ch_id, "bob").unwrap();
7768
7769        // Send two messages
7770        store.send_message(ch_id, "alice", "msg1", MessageType::Text).unwrap();
7771        store.send_message(ch_id, "alice", "msg2", MessageType::Text).unwrap();
7772        let lamport_after_send = store.lamport_counter;
7773
7774        // Receive them — should merge lamport (though they're from the same store,
7775        // the mechanism works)
7776        let msgs = store.receive_messages(ch_id, None, None).unwrap();
7777        assert_eq!(msgs.len(), 2);
7778        // lamport_counter should be at least as high as after sending
7779        assert!(store.lamport_counter >= lamport_after_send);
7780    }
7781
7782    #[test]
7783    fn test_channel_config_backward_compat_no_min_trust() {
7784        // Ensure ChannelConfig without min_trust_level deserializes correctly
7785        let json = r#"{
7786            "max_participants": 10,
7787            "ttl_seconds": 0,
7788            "persistence": true,
7789            "encryption_required": false
7790        }"#;
7791        let config: ChannelConfig = serde_json::from_str(json).unwrap();
7792        assert_eq!(config.min_trust_level, None);
7793    }
7794
7795    // -----------------------------------------------------------------------
7796    // Agent registry tests
7797    // -----------------------------------------------------------------------
7798
7799    #[test]
7800    fn test_agent_registry_basic() {
7801        let mut store = CommStore::new();
7802        let agent = CommunicatingAgent {
7803            agent_id: "agent-1".to_string(),
7804            availability: Availability::Available,
7805            ..Default::default()
7806        };
7807        store.register_agent(agent).unwrap();
7808        assert!(store.get_agent("agent-1").is_some());
7809        assert_eq!(store.list_agents().len(), 1);
7810    }
7811
7812    #[test]
7813    fn test_agent_availability_update() {
7814        let mut store = CommStore::new();
7815        let agent = CommunicatingAgent {
7816            agent_id: "agent-1".to_string(),
7817            availability: Availability::Available,
7818            ..Default::default()
7819        };
7820        store.register_agent(agent).unwrap();
7821        store
7822            .update_agent_availability("agent-1", Availability::Busy)
7823            .unwrap();
7824        assert_eq!(
7825            store.get_agent("agent-1").unwrap().availability,
7826            Availability::Busy
7827        );
7828    }
7829
7830    #[test]
7831    fn test_agent_unregister() {
7832        let mut store = CommStore::new();
7833        let agent = CommunicatingAgent {
7834            agent_id: "agent-1".to_string(),
7835            ..Default::default()
7836        };
7837        store.register_agent(agent).unwrap();
7838        store.unregister_agent("agent-1").unwrap();
7839        assert!(store.get_agent("agent-1").is_none());
7840        assert!(store.unregister_agent("agent-1").is_err());
7841    }
7842
7843    #[test]
7844    fn test_bridge_config_set() {
7845        let mut store = CommStore::new();
7846        let config = BridgeConfig {
7847            identity_enabled: true,
7848            memory_enabled: true,
7849            ..Default::default()
7850        };
7851        store.set_bridge_config(config);
7852        assert!(store.bridge_config.identity_enabled);
7853        assert!(store.bridge_config.memory_enabled);
7854        assert!(!store.bridge_config.time_enabled);
7855    }
7856
7857    #[test]
7858    fn test_agent_update_nonexistent() {
7859        let mut store = CommStore::new();
7860        let result = store.update_agent_availability("ghost", Availability::Busy);
7861        assert!(result.is_err());
7862    }
7863
7864    #[test]
7865    fn test_agent_registry_audit_log() {
7866        let mut store = CommStore::new();
7867        let agent = CommunicatingAgent {
7868            agent_id: "agent-audit".to_string(),
7869            ..Default::default()
7870        };
7871        store.register_agent(agent).unwrap();
7872        store.unregister_agent("agent-audit").unwrap();
7873
7874        // Should have at least 2 audit entries (register + unregister)
7875        let register_entries: Vec<_> = store
7876            .audit_log
7877            .iter()
7878            .filter(|e| e.event_type == AuditEventType::AgentRegistered)
7879            .collect();
7880        let unregister_entries: Vec<_> = store
7881            .audit_log
7882            .iter()
7883            .filter(|e| e.event_type == AuditEventType::AgentUnregistered)
7884            .collect();
7885        assert_eq!(register_entries.len(), 1);
7886        assert_eq!(unregister_entries.len(), 1);
7887    }
7888
7889    #[test]
7890    fn test_agents_serde_backward_compat() {
7891        // A CommStore serialized without agents should deserialize fine
7892        let store = CommStore::new();
7893        let json = serde_json::to_string(&store).unwrap();
7894        let deserialized: CommStore = serde_json::from_str(&json).unwrap();
7895        assert!(deserialized.agents.is_empty());
7896    }
7897}