Skip to main content

feltdb/
capability.rs

1use crate::Operation;
2/// Capability and Extension Runtime module
3///
4/// Capabilities are extensible projections that consume the FeltDB operation stream
5/// and maintain rebuildable derived state. They follow a generic contract:
6///
7/// canonical operation → capability → derived state → capability query
8///
9/// This keeps the core log generic while allowing specialized behavior.
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::collections::HashMap;
13use std::fmt;
14
15/// Unique capability identifier
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
17pub struct CapabilityId(pub String);
18
19impl fmt::Display for CapabilityId {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "{}", self.0)
22    }
23}
24
25impl CapabilityId {
26    pub fn new(id: String) -> Self {
27        CapabilityId(id)
28    }
29}
30
31/// The roles a capability can fulfill
32#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
33pub enum CapabilityRole {
34    /// Triggers and executes business logic
35    ExecutionEngine,
36    /// Maintains a projection/index of canonical state
37    Projection,
38    /// Both executes and maintains a projection
39    ExecutionWithProjection,
40}
41
42/// Lifecycle states for a capability
43#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
44pub enum CapabilityLifecycleState {
45    /// Capability has been registered but not yet initialized
46    Registered,
47    /// Capability is initializing its state
48    Initializing,
49    /// Capability is catching up with historical operations
50    CatchingUp,
51    /// Capability is active and processing new operations
52    Active,
53    /// Capability is paused
54    Paused,
55    /// Capability is being rebuilt
56    Rebuilding,
57    /// Capability has been removed
58    Removed,
59}
60
61/// Context passed to capability operations
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct CapabilityContext {
64    /// The capability ID
65    pub capability_id: CapabilityId,
66    /// Instance ID of the FeltDB instance
67    pub instance_id: String,
68    /// Current sequence number
69    pub sequence: u64,
70    /// Capability version
71    pub version: u32,
72}
73
74/// Query structure for capability queries
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct CapabilityQuery {
77    /// Query type (e.g., "search", "vector_find", "analytics")
78    pub query_type: String,
79    /// Query parameters
80    pub params: Value,
81}
82
83impl CapabilityQuery {
84    pub fn new(query_type: String, params: Value) -> Self {
85        Self { query_type, params }
86    }
87}
88
89/// Checkpoint for capability progress tracking
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct CapabilityCheckpoint {
92    /// The capability ID
93    pub capability_id: CapabilityId,
94    /// Last processed instance ID
95    pub last_processed_instance_id: String,
96    /// Last processed sequence number
97    pub last_processed_sequence: u64,
98    /// Current lifecycle state
99    pub lifecycle_state: CapabilityLifecycleState,
100    /// Timestamp of last checkpoint (ms since UNIX epoch)
101    pub checkpoint_ms: u128,
102    /// Capability version
103    pub version: u32,
104}
105
106impl CapabilityCheckpoint {
107    pub fn new(capability_id: CapabilityId, instance_id: String) -> Self {
108        Self {
109            capability_id,
110            last_processed_instance_id: instance_id,
111            last_processed_sequence: 0,
112            lifecycle_state: CapabilityLifecycleState::Registered,
113            checkpoint_ms: crate::now_ms(),
114            version: 1,
115        }
116    }
117
118    /// Update checkpoint with new processed position
119    pub fn update_position(&mut self, instance_id: String, sequence: u64) {
120        self.last_processed_instance_id = instance_id;
121        self.last_processed_sequence = sequence;
122        self.checkpoint_ms = crate::now_ms();
123    }
124
125    /// Set lifecycle state
126    pub fn set_state(&mut self, state: CapabilityLifecycleState) {
127        self.lifecycle_state = state;
128        self.checkpoint_ms = crate::now_ms();
129    }
130}
131
132/// Result of processing an operation through a capability
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct CapabilityOperationResult {
135    /// Whether the operation was successfully processed
136    pub success: bool,
137    /// Optional error message
138    pub error: Option<String>,
139    /// Optional result data
140    pub result: Option<Value>,
141    /// Whether this capability produced derived state
142    pub produced_derived_state: bool,
143}
144
145impl CapabilityOperationResult {
146    pub fn success() -> Self {
147        Self {
148            success: true,
149            error: None,
150            result: None,
151            produced_derived_state: false,
152        }
153    }
154
155    pub fn with_derived_state(result: Value) -> Self {
156        Self {
157            success: true,
158            error: None,
159            result: Some(result),
160            produced_derived_state: true,
161        }
162    }
163
164    pub fn error(msg: String) -> Self {
165        Self {
166            success: false,
167            error: Some(msg),
168            result: None,
169            produced_derived_state: false,
170        }
171    }
172}
173
174/// Result of a capability query
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct CapabilityQueryResult {
177    /// Whether the query succeeded
178    pub success: bool,
179    /// Query results
180    pub results: Vec<Value>,
181    /// Optional error message
182    pub error: Option<String>,
183}
184
185impl CapabilityQueryResult {
186    pub fn success(results: Vec<Value>) -> Self {
187        Self {
188            success: true,
189            results,
190            error: None,
191        }
192    }
193
194    pub fn empty() -> Self {
195        Self {
196            success: true,
197            results: Vec::new(),
198            error: None,
199        }
200    }
201
202    pub fn error(msg: String) -> Self {
203        Self {
204            success: false,
205            results: Vec::new(),
206            error: Some(msg),
207        }
208    }
209}
210
211/// Access model for capability security
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct CapabilityAccessModel {
214    /// Collections this capability can read from
215    pub reads: Vec<String>,
216    /// Collections this capability can write to
217    pub writes: Vec<String>,
218    /// External systems this capability can execute
219    pub executes: Vec<String>,
220}
221
222impl CapabilityAccessModel {
223    pub fn new(reads: Vec<String>, writes: Vec<String>, executes: Vec<String>) -> Self {
224        Self {
225            reads,
226            writes,
227            executes,
228        }
229    }
230
231    /// Check if capability can read from collection
232    pub fn can_read(&self, collection: &str) -> bool {
233        self.reads.contains(&collection.to_string()) || self.reads.iter().any(|r| r == "*")
234    }
235
236    /// Check if capability can write to collection
237    pub fn can_write(&self, collection: &str) -> bool {
238        self.writes.contains(&collection.to_string()) || self.writes.iter().any(|w| w == "*")
239    }
240
241    /// Check if capability can execute external system
242    pub fn can_execute(&self, system: &str) -> bool {
243        self.executes.contains(&system.to_string()) || self.executes.iter().any(|e| e == "*")
244    }
245}
246
247/// Capability metadata and configuration
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct CapabilityMetadata {
250    pub id: CapabilityId,
251    pub name: String,
252    pub description: String,
253    pub role: CapabilityRole,
254    pub access_model: CapabilityAccessModel,
255    pub version: u32,
256    pub created_ms: u128,
257}
258
259impl CapabilityMetadata {
260    pub fn new(
261        id: CapabilityId,
262        name: String,
263        description: String,
264        role: CapabilityRole,
265        access_model: CapabilityAccessModel,
266    ) -> Self {
267        Self {
268            id,
269            name,
270            description,
271            role,
272            access_model,
273            version: 1,
274            created_ms: crate::now_ms(),
275        }
276    }
277}
278
279/// Capability registry - tracks all capabilities
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct CapabilityRegistry {
282    /// All capabilities by ID
283    #[serde(default)]
284    capabilities: HashMap<CapabilityId, CapabilityMetadata>,
285    /// Checkpoints by capability ID
286    #[serde(default)]
287    checkpoints: HashMap<CapabilityId, CapabilityCheckpoint>,
288}
289
290impl CapabilityRegistry {
291    pub fn new() -> Self {
292        Self {
293            capabilities: HashMap::new(),
294            checkpoints: HashMap::new(),
295        }
296    }
297
298    /// Register a new capability
299    pub fn register(&mut self, metadata: CapabilityMetadata) -> crate::Result<()> {
300        let id = metadata.id.clone();
301        if self.capabilities.contains_key(&id) {
302            return Err(crate::FlowError::CapabilityError(
303                "Capability already registered".to_string(),
304            ));
305        }
306        let checkpoint = CapabilityCheckpoint::new(id.clone(), String::new());
307        self.capabilities.insert(id.clone(), metadata);
308        self.checkpoints.insert(id, checkpoint);
309        Ok(())
310    }
311
312    /// Get capability metadata
313    pub fn get(&self, id: &CapabilityId) -> Option<&CapabilityMetadata> {
314        self.capabilities.get(id)
315    }
316
317    /// Get capability checkpoint
318    pub fn get_checkpoint(&self, id: &CapabilityId) -> Option<&CapabilityCheckpoint> {
319        self.checkpoints.get(id)
320    }
321
322    /// Update capability checkpoint
323    pub fn update_checkpoint(&mut self, checkpoint: CapabilityCheckpoint) {
324        self.checkpoints
325            .insert(checkpoint.capability_id.clone(), checkpoint);
326    }
327
328    /// List all capabilities
329    pub fn list(&self) -> Vec<&CapabilityMetadata> {
330        self.capabilities.values().collect()
331    }
332
333    /// Remove a capability
334    pub fn remove(&mut self, id: &CapabilityId) -> Option<CapabilityMetadata> {
335        self.checkpoints.remove(id);
336        self.capabilities.remove(id)
337    }
338}
339
340impl Default for CapabilityRegistry {
341    fn default() -> Self {
342        Self::new()
343    }
344}
345
346/// Trait for implementing capabilities
347/// This is the core contract for extending FeltDB with specialized behavior
348pub trait FlowCapability: Send + Sync {
349    /// Get the capability ID
350    fn id(&self) -> CapabilityId;
351
352    /// Get the capability metadata
353    fn metadata(&self) -> CapabilityMetadata;
354
355    /// Initialize the capability (called on registration)
356    fn initialize(&mut self, context: &CapabilityContext) -> crate::Result<()>;
357
358    /// Process an operation - maintain derived state
359    fn on_operation(
360        &mut self,
361        operation: &Operation,
362        context: &CapabilityContext,
363    ) -> crate::Result<CapabilityOperationResult>;
364
365    /// Query the capability's derived state
366    fn query(
367        &self,
368        query: &CapabilityQuery,
369        context: &CapabilityContext,
370    ) -> crate::Result<CapabilityQueryResult>;
371
372    /// Rebuild the capability's derived state from scratch
373    fn rebuild(
374        &mut self,
375        operations: &[Operation],
376        context: &CapabilityContext,
377    ) -> crate::Result<()>;
378
379    /// Pause the capability
380    fn pause(&mut self) -> crate::Result<()> {
381        Ok(())
382    }
383
384    /// Resume the capability
385    fn resume(&mut self) -> crate::Result<()> {
386        Ok(())
387    }
388
389    /// Check if the capability is ready (fully caught up)
390    fn is_ready(&self) -> bool {
391        true
392    }
393}
394
395/// Reference to a capability with namespace and version
396/// Canonical form: flow://capability/namespace:name@version
397#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
398pub struct CapabilityRef {
399    /// Namespace (e.g., "docs", "images")
400    pub namespace: String,
401    /// Capability name (e.g., "vector-search", "image-analysis")
402    pub capability: String,
403    /// Capability version
404    pub version: u32,
405}
406
407impl CapabilityRef {
408    /// Create a new capability reference
409    pub fn new(namespace: String, capability: String, version: u32) -> Self {
410        Self {
411            namespace,
412            capability,
413            version,
414        }
415    }
416
417    /// Get canonical form for serialization
418    pub fn canonical(&self) -> String {
419        format!(
420            "flow://capability/{}/{}@{}",
421            self.namespace, self.capability, self.version
422        )
423    }
424
425    /// Parse from canonical form
426    pub fn from_canonical(canonical: &str) -> crate::Result<Self> {
427        if !canonical.starts_with("flow://capability/") {
428            return Err(crate::FlowError::CapabilityError(
429                "Invalid capability reference format".to_string(),
430            ));
431        }
432
433        let rest = &canonical[18..]; // Skip "flow://capability/"
434        let parts: Vec<&str> = rest.split('/').collect();
435        if parts.len() != 2 {
436            return Err(crate::FlowError::CapabilityError(
437                "Invalid capability reference format".to_string(),
438            ));
439        }
440
441        let namespace = parts[0].to_string();
442        let cap_parts: Vec<&str> = parts[1].split('@').collect();
443        if cap_parts.len() != 2 {
444            return Err(crate::FlowError::CapabilityError(
445                "Invalid capability reference format".to_string(),
446            ));
447        }
448
449        let capability = cap_parts[0].to_string();
450        let version = cap_parts[1]
451            .parse::<u32>()
452            .map_err(|_| crate::FlowError::CapabilityError("Invalid version number".to_string()))?;
453
454        Ok(Self::new(namespace, capability, version))
455    }
456}
457
458impl fmt::Display for CapabilityRef {
459    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460        write!(f, "{}", self.canonical())
461    }
462}
463
464/// Represents a required state for a capability to execute
465#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
466pub struct StateRequirement {
467    /// Namespace containing the required state
468    pub namespace: String,
469    /// Minimum version required (0 means any)
470    pub min_version: u64,
471}
472
473impl StateRequirement {
474    pub fn new(namespace: String, min_version: u64) -> Self {
475        Self {
476            namespace,
477            min_version,
478        }
479    }
480}
481
482/// Requirements for a capability to execute
483#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct CapabilityRequirements {
485    /// Required namespaces and minimum versions
486    pub required_state: Vec<StateRequirement>,
487    /// Maximum acceptable latency for state (ms)
488    pub max_state_latency_ms: Option<u64>,
489    /// Whether execution requires single-owner semantics
490    pub requires_ownership: bool,
491}
492
493impl CapabilityRequirements {
494    pub fn new() -> Self {
495        Self {
496            required_state: Vec::new(),
497            max_state_latency_ms: None,
498            requires_ownership: false,
499        }
500    }
501
502    pub fn with_required_state(mut self, requirement: StateRequirement) -> Self {
503        self.required_state.push(requirement);
504        self
505    }
506
507    pub fn with_max_latency(mut self, latency_ms: u64) -> Self {
508        self.max_state_latency_ms = Some(latency_ms);
509        self
510    }
511
512    pub fn with_ownership_requirement(mut self, requires: bool) -> Self {
513        self.requires_ownership = requires;
514        self
515    }
516}
517
518impl Default for CapabilityRequirements {
519    fn default() -> Self {
520        Self::new()
521    }
522}
523
524/// Advertises a capability at a specific peer with its requirements
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct DistributedCapabilityAdvertisement {
527    /// The capability reference
528    pub capability_ref: CapabilityRef,
529    /// Peer ID providing this capability
530    pub peer_id: String,
531    /// Requirements for this capability
532    pub requirements: CapabilityRequirements,
533    /// Timestamp of advertisement
534    pub advertised_ms: u128,
535}
536
537impl DistributedCapabilityAdvertisement {
538    pub fn new(capability_ref: CapabilityRef, peer_id: String) -> Self {
539        Self {
540            capability_ref,
541            peer_id,
542            requirements: CapabilityRequirements::new(),
543            advertised_ms: crate::now_ms(),
544        }
545    }
546
547    pub fn with_requirements(mut self, requirements: CapabilityRequirements) -> Self {
548        self.requirements = requirements;
549        self
550    }
551}
552
553/// Context for executing a capability (local or remote)
554#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct CapabilityExecutionContext {
556    /// Unique execution ID
557    pub execution_id: String,
558    /// The capability being executed
559    pub capability_ref: CapabilityRef,
560    /// References to input data (instead of serialized payloads)
561    pub input_refs: Vec<String>,
562    /// Required state version for execution
563    pub required_state_version: u64,
564    /// Requester instance/peer ID
565    pub requester: String,
566    /// Timestamp when execution was requested
567    pub requested_ms: u128,
568}
569
570impl CapabilityExecutionContext {
571    pub fn new(execution_id: String, capability_ref: CapabilityRef, requester: String) -> Self {
572        Self {
573            execution_id,
574            capability_ref,
575            input_refs: Vec::new(),
576            required_state_version: 0,
577            requester,
578            requested_ms: crate::now_ms(),
579        }
580    }
581
582    pub fn with_input_ref(mut self, input_ref: String) -> Self {
583        self.input_refs.push(input_ref);
584        self
585    }
586
587    pub fn with_required_state_version(mut self, version: u64) -> Self {
588        self.required_state_version = version;
589        self
590    }
591}
592
593/// Reference to an execution result with provenance
594#[derive(Debug, Clone, Serialize, Deserialize)]
595pub struct ExecutionRef {
596    /// Unique execution ID
597    pub execution_id: String,
598    /// The capability that was executed
599    pub capability_ref: CapabilityRef,
600    /// Peer that executed the capability
601    pub provider_peer: String,
602    /// Input references used
603    pub input_refs: Vec<String>,
604    /// State version at time of execution
605    pub input_state_version: u64,
606    /// Content identity of the result
607    pub result_content_identity: String,
608    /// Operation that recorded this result
609    pub created_operation: Option<String>,
610}
611
612impl ExecutionRef {
613    pub fn new(execution_id: String, capability_ref: CapabilityRef, provider_peer: String) -> Self {
614        Self {
615            execution_id,
616            capability_ref,
617            provider_peer,
618            input_refs: Vec::new(),
619            input_state_version: 0,
620            result_content_identity: String::new(),
621            created_operation: None,
622        }
623    }
624}
625
626/// Result of a capability execution with full provenance
627#[derive(Debug, Clone, Serialize, Deserialize)]
628pub struct CapabilityExecutionResult {
629    /// Reference to the execution
630    pub execution_ref: ExecutionRef,
631    /// The actual result data
632    pub result: Value,
633    /// Timestamp when execution completed
634    pub completed_ms: u128,
635    /// Any error that occurred
636    pub error: Option<String>,
637}
638
639impl CapabilityExecutionResult {
640    pub fn success(execution_ref: ExecutionRef, result: Value) -> Self {
641        Self {
642            execution_ref,
643            result,
644            completed_ms: crate::now_ms(),
645            error: None,
646        }
647    }
648
649    pub fn error(execution_ref: ExecutionRef, error: String) -> Self {
650        Self {
651            execution_ref,
652            result: Value::Null,
653            completed_ms: crate::now_ms(),
654            error: Some(error),
655        }
656    }
657}
658
659/// Resolver for capabilities - routes to appropriate provider
660pub struct CapabilityResolver {
661    /// Registered capabilities by ref
662    capabilities: HashMap<String, DistributedCapabilityAdvertisement>,
663}
664
665impl CapabilityResolver {
666    pub fn new() -> Self {
667        Self {
668            capabilities: HashMap::new(),
669        }
670    }
671
672    /// Register a capability advertisement
673    pub fn register(&mut self, ad: DistributedCapabilityAdvertisement) {
674        self.capabilities.insert(ad.capability_ref.canonical(), ad);
675    }
676
677    /// Find a provider for a capability
678    pub fn resolve(&self, cap_ref: &CapabilityRef) -> Option<&DistributedCapabilityAdvertisement> {
679        self.capabilities.get(&cap_ref.canonical())
680    }
681
682    /// Find all providers for a capability (by name only)
683    pub fn resolve_all(
684        &self,
685        namespace: &str,
686        capability: &str,
687    ) -> Vec<&DistributedCapabilityAdvertisement> {
688        self.capabilities
689            .values()
690            .filter(|ad| {
691                ad.capability_ref.namespace == namespace
692                    && ad.capability_ref.capability == capability
693            })
694            .collect()
695    }
696}
697
698impl Default for CapabilityResolver {
699    fn default() -> Self {
700        Self::new()
701    }
702}
703
704#[cfg(test)]
705mod tests {
706    use super::*;
707
708    #[test]
709    fn test_capability_id_creation() {
710        let id = CapabilityId::new("search".to_string());
711        assert_eq!(id.0, "search");
712    }
713
714    #[test]
715    fn test_capability_checkpoint() {
716        let mut checkpoint = CapabilityCheckpoint::new(
717            CapabilityId::new("search".to_string()),
718            "instance-1".to_string(),
719        );
720        assert_eq!(checkpoint.last_processed_sequence, 0);
721
722        checkpoint.update_position("instance-1".to_string(), 100);
723        assert_eq!(checkpoint.last_processed_sequence, 100);
724    }
725
726    #[test]
727    fn test_capability_access_model() {
728        let access = CapabilityAccessModel::new(
729            vec!["docs".to_string()],
730            vec!["docs.search".to_string()],
731            vec!["embedding-api".to_string()],
732        );
733
734        assert!(access.can_read("docs"));
735        assert!(!access.can_read("users"));
736        assert!(access.can_write("docs.search"));
737        assert!(access.can_execute("embedding-api"));
738    }
739
740    #[test]
741    fn test_capability_access_wildcard() {
742        let access =
743            CapabilityAccessModel::new(vec!["*".to_string()], vec!["*".to_string()], vec![]);
744
745        assert!(access.can_read("anything"));
746        assert!(access.can_write("anything"));
747    }
748
749    #[test]
750    fn test_capability_registry() {
751        let mut registry = CapabilityRegistry::new();
752        let metadata = CapabilityMetadata::new(
753            CapabilityId::new("search".to_string()),
754            "Search Capability".to_string(),
755            "Full-text search".to_string(),
756            CapabilityRole::Projection,
757            CapabilityAccessModel::new(
758                vec!["docs".to_string()],
759                vec!["docs.search".to_string()],
760                vec![],
761            ),
762        );
763
764        registry.register(metadata).unwrap();
765        let cap_id = CapabilityId::new("search".to_string());
766        assert!(registry.get(&cap_id).is_some());
767        assert!(registry.get_checkpoint(&cap_id).is_some());
768    }
769
770    #[test]
771    fn test_capability_ref_creation_and_canonical() {
772        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
773        assert_eq!(cap_ref.namespace, "docs");
774        assert_eq!(cap_ref.capability, "vector-search");
775        assert_eq!(cap_ref.version, 1);
776        assert_eq!(
777            cap_ref.canonical(),
778            "flow://capability/docs/vector-search@1"
779        );
780    }
781
782    #[test]
783    fn test_capability_ref_parse_canonical() {
784        let canonical = "flow://capability/docs/vector-search@1";
785        let cap_ref = CapabilityRef::from_canonical(canonical).unwrap();
786        assert_eq!(cap_ref.namespace, "docs");
787        assert_eq!(cap_ref.capability, "vector-search");
788        assert_eq!(cap_ref.version, 1);
789    }
790
791    #[test]
792    fn test_capability_ref_display() {
793        let cap_ref = CapabilityRef::new("images".to_string(), "image-analysis".to_string(), 2);
794        assert_eq!(
795            cap_ref.to_string(),
796            "flow://capability/images/image-analysis@2"
797        );
798    }
799
800    #[test]
801    fn test_state_requirement_creation() {
802        let req = StateRequirement::new("docs".to_string(), 847);
803        assert_eq!(req.namespace, "docs");
804        assert_eq!(req.min_version, 847);
805    }
806
807    #[test]
808    fn test_capability_requirements_builder() {
809        let reqs = CapabilityRequirements::new()
810            .with_required_state(StateRequirement::new("docs".to_string(), 847))
811            .with_max_latency(5000)
812            .with_ownership_requirement(true);
813
814        assert_eq!(reqs.required_state.len(), 1);
815        assert_eq!(reqs.max_state_latency_ms, Some(5000));
816        assert!(reqs.requires_ownership);
817    }
818
819    #[test]
820    fn test_distributed_capability_advertisement() {
821        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
822        let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string())
823            .with_requirements(
824                CapabilityRequirements::new()
825                    .with_required_state(StateRequirement::new("docs".to_string(), 847)),
826            );
827
828        assert_eq!(ad.capability_ref, cap_ref);
829        assert_eq!(ad.peer_id, "peer-b");
830        assert_eq!(ad.requirements.required_state.len(), 1);
831    }
832
833    #[test]
834    fn test_capability_execution_context() {
835        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
836        let ctx = CapabilityExecutionContext::new(
837            "exec-123".to_string(),
838            cap_ref.clone(),
839            "requester".to_string(),
840        )
841        .with_input_ref("flow://docs#123".to_string())
842        .with_required_state_version(851);
843
844        assert_eq!(ctx.execution_id, "exec-123");
845        assert_eq!(ctx.capability_ref, cap_ref);
846        assert_eq!(ctx.input_refs.len(), 1);
847        assert_eq!(ctx.required_state_version, 851);
848    }
849
850    #[test]
851    fn test_execution_ref_creation() {
852        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
853        let exec_ref = ExecutionRef::new(
854            "exec-123".to_string(),
855            cap_ref.clone(),
856            "peer-b".to_string(),
857        );
858
859        assert_eq!(exec_ref.execution_id, "exec-123");
860        assert_eq!(exec_ref.capability_ref, cap_ref);
861        assert_eq!(exec_ref.provider_peer, "peer-b");
862    }
863
864    #[test]
865    fn test_capability_execution_result_success() {
866        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
867        let exec_ref = ExecutionRef::new("exec-123".to_string(), cap_ref, "peer-b".to_string());
868        let result = serde_json::json!({"results": [1, 2, 3]});
869
870        let exec_result = CapabilityExecutionResult::success(exec_ref.clone(), result.clone());
871        assert!(exec_result.error.is_none());
872        assert_eq!(exec_result.result, result);
873    }
874
875    #[test]
876    fn test_capability_resolver_registration() {
877        let mut resolver = CapabilityResolver::new();
878        let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
879        let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string());
880
881        resolver.register(ad);
882
883        let resolved = resolver.resolve(&cap_ref);
884        assert!(resolved.is_some());
885        assert_eq!(resolved.unwrap().peer_id, "peer-b");
886    }
887
888    #[test]
889    fn test_capability_resolver_resolve_all() {
890        let mut resolver = CapabilityResolver::new();
891
892        let cap_ref_v1 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
893        let ad1 = DistributedCapabilityAdvertisement::new(cap_ref_v1, "peer-a".to_string());
894        resolver.register(ad1);
895
896        let cap_ref_v2 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 2);
897        let ad2 = DistributedCapabilityAdvertisement::new(cap_ref_v2, "peer-b".to_string());
898        resolver.register(ad2);
899
900        let all = resolver.resolve_all("docs", "vector-search");
901        assert_eq!(all.len(), 2);
902    }
903}