Skip to main content

oxirs_core/stability/
mod.rs

1//! # API Stability Markers for OxiRS
2//!
3//! This module provides stability documentation and tracking for OxiRS public APIs,
4//! following Rust API guidelines and SemVer compatibility promises.
5
6pub mod compatibility;
7
8/// API stability levels for OxiRS public interfaces.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum StabilityLevel {
11    /// Guaranteed backward compatible through the 1.x.y series.
12    Stable,
13    /// Likely stable but may change in minor releases.
14    Beta,
15    /// May change or be removed in any release.
16    Experimental,
17    /// Scheduled for removal in the next major version.
18    Deprecated {
19        /// The version in which this API was deprecated.
20        since: &'static str,
21        /// The recommended replacement API, if one exists.
22        replacement: Option<&'static str>,
23    },
24}
25
26impl StabilityLevel {
27    /// Returns true if the API is considered production-ready (Stable or Beta).
28    pub fn is_production_ready(&self) -> bool {
29        matches!(
30            self,
31            StabilityLevel::Stable | StabilityLevel::Beta | StabilityLevel::Deprecated { .. }
32        )
33    }
34
35    /// Returns a short descriptive label for the stability level.
36    pub fn label(&self) -> &'static str {
37        match self {
38            StabilityLevel::Stable => "Stable",
39            StabilityLevel::Beta => "Beta",
40            StabilityLevel::Experimental => "Experimental",
41            StabilityLevel::Deprecated { .. } => "Deprecated",
42        }
43    }
44
45    /// Returns a bracket indicator for report formatting.
46    pub fn indicator(&self) -> &'static str {
47        match self {
48            StabilityLevel::Stable => "[STABLE]",
49            StabilityLevel::Beta => "[BETA]",
50            StabilityLevel::Experimental => "[EXPERIMENTAL]",
51            StabilityLevel::Deprecated { .. } => "[DEPRECATED]",
52        }
53    }
54}
55
56/// The broad functional category of an OxiRS API.
57#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub enum ApiCategory {
59    QueryLanguage,
60    DataModel,
61    Reasoning,
62    GraphQuery,
63    Geospatial,
64    Validation,
65    Storage,
66    Streaming,
67    Industrial,
68    ArtificialIntelligence,
69    Security,
70    RdfStar,
71    Federation,
72    Server,
73    WebAssembly,
74    TimeSeries,
75    PhysicsSimulation,
76    Quantum,
77    Tooling,
78}
79
80impl ApiCategory {
81    /// Returns a human-readable label for the category.
82    pub fn label(&self) -> &'static str {
83        match self {
84            ApiCategory::QueryLanguage => "Query Language",
85            ApiCategory::DataModel => "Data Model",
86            ApiCategory::Reasoning => "Reasoning",
87            ApiCategory::GraphQuery => "Graph Query",
88            ApiCategory::Geospatial => "Geospatial",
89            ApiCategory::Validation => "Validation",
90            ApiCategory::Storage => "Storage",
91            ApiCategory::Streaming => "Streaming",
92            ApiCategory::Industrial => "Industrial",
93            ApiCategory::ArtificialIntelligence => "Artificial Intelligence",
94            ApiCategory::Security => "Security",
95            ApiCategory::RdfStar => "RDF-star",
96            ApiCategory::Federation => "Federation",
97            ApiCategory::Server => "Server",
98            ApiCategory::WebAssembly => "WebAssembly",
99            ApiCategory::TimeSeries => "Time-Series",
100            ApiCategory::PhysicsSimulation => "Physics Simulation",
101            ApiCategory::Quantum => "Quantum",
102            ApiCategory::Tooling => "Tooling",
103        }
104    }
105}
106
107/// A single API stability marker recording the stability commitment for one feature.
108#[derive(Debug, Clone)]
109pub struct ApiStabilityMarker {
110    /// The feature name.
111    pub feature: &'static str,
112    /// The functional category this feature belongs to.
113    pub category: ApiCategory,
114    /// The stability guarantee for this feature.
115    pub level: StabilityLevel,
116    /// The version in which this API was introduced.
117    pub since: &'static str,
118    /// Human-readable description of the feature and its stability rationale.
119    pub description: &'static str,
120    /// Optional link to specification or documentation.
121    pub spec_url: Option<&'static str>,
122}
123
124impl ApiStabilityMarker {
125    /// Creates a Stable API marker.
126    pub fn stable(
127        feature: &'static str,
128        category: ApiCategory,
129        since: &'static str,
130        description: &'static str,
131    ) -> Self {
132        Self {
133            feature,
134            category,
135            level: StabilityLevel::Stable,
136            since,
137            description,
138            spec_url: None,
139        }
140    }
141
142    /// Creates a Beta API marker.
143    pub fn beta(
144        feature: &'static str,
145        category: ApiCategory,
146        since: &'static str,
147        description: &'static str,
148    ) -> Self {
149        Self {
150            feature,
151            category,
152            level: StabilityLevel::Beta,
153            since,
154            description,
155            spec_url: None,
156        }
157    }
158
159    /// Creates an Experimental API marker.
160    pub fn experimental(
161        feature: &'static str,
162        category: ApiCategory,
163        since: &'static str,
164        description: &'static str,
165    ) -> Self {
166        Self {
167            feature,
168            category,
169            level: StabilityLevel::Experimental,
170            since,
171            description,
172            spec_url: None,
173        }
174    }
175
176    /// Creates a Deprecated API marker.
177    pub fn deprecated(
178        feature: &'static str,
179        category: ApiCategory,
180        since: &'static str,
181        deprecated_since: &'static str,
182        replacement: Option<&'static str>,
183        description: &'static str,
184    ) -> Self {
185        Self {
186            feature,
187            category,
188            level: StabilityLevel::Deprecated {
189                since: deprecated_since,
190                replacement,
191            },
192            since,
193            description,
194            spec_url: None,
195        }
196    }
197
198    /// Attaches a specification URL (builder pattern).
199    pub fn with_spec(mut self, url: &'static str) -> Self {
200        self.spec_url = Some(url);
201        self
202    }
203
204    /// Returns true if this marker is at the Stable level.
205    pub fn is_stable(&self) -> bool {
206        matches!(self.level, StabilityLevel::Stable)
207    }
208
209    /// Returns true if this marker is at the Beta level.
210    pub fn is_beta(&self) -> bool {
211        matches!(self.level, StabilityLevel::Beta)
212    }
213
214    /// Returns true if this marker is at the Experimental level.
215    pub fn is_experimental(&self) -> bool {
216        matches!(self.level, StabilityLevel::Experimental)
217    }
218
219    /// Returns true if this marker is Deprecated.
220    pub fn is_deprecated(&self) -> bool {
221        matches!(self.level, StabilityLevel::Deprecated { .. })
222    }
223}
224
225/// Summary statistics for a stability registry.
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct RegistrySummary {
228    pub total: usize,
229    pub stable_count: usize,
230    pub beta_count: usize,
231    pub experimental_count: usize,
232    pub deprecated_count: usize,
233}
234
235impl RegistrySummary {
236    /// Percentage of APIs that are Stable (0-100).
237    pub fn stable_percentage(&self) -> f64 {
238        if self.total == 0 {
239            return 0.0;
240        }
241        (self.stable_count as f64 / self.total as f64) * 100.0
242    }
243
244    /// Percentage of APIs that are production-ready (Stable + Beta + Deprecated).
245    pub fn production_ready_percentage(&self) -> f64 {
246        if self.total == 0 {
247            return 0.0;
248        }
249        ((self.stable_count + self.beta_count + self.deprecated_count) as f64 / self.total as f64)
250            * 100.0
251    }
252}
253
254/// Registry of all OxiRS public API stability commitments.
255pub struct StabilityRegistry {
256    markers: Vec<ApiStabilityMarker>,
257}
258
259impl StabilityRegistry {
260    /// Creates an empty registry.
261    pub fn new() -> Self {
262        Self {
263            markers: Vec::new(),
264        }
265    }
266
267    /// Registers a new API stability marker.
268    pub fn register(&mut self, marker: ApiStabilityMarker) {
269        self.markers.push(marker);
270    }
271
272    /// Returns the complete OxiRS v1 stability registry.
273    pub fn oxirs_v1_stability() -> Self {
274        let mut r = Self::new();
275
276        // SPARQL Query Language
277        r.register(ApiStabilityMarker::stable("SPARQL 1.1 SELECT", ApiCategory::QueryLanguage, "0.1.0",
278            "Full SPARQL 1.1 SELECT query support including projections, DISTINCT, ORDER BY, LIMIT, OFFSET, GROUP BY, HAVING, and aggregate functions.")
279            .with_spec("https://www.w3.org/TR/sparql11-query/"));
280        r.register(
281            ApiStabilityMarker::stable(
282                "SPARQL 1.1 ASK",
283                ApiCategory::QueryLanguage,
284                "0.1.0",
285                "SPARQL 1.1 ASK queries returning boolean existence results.",
286            )
287            .with_spec("https://www.w3.org/TR/sparql11-query/"),
288        );
289        r.register(
290            ApiStabilityMarker::stable(
291                "SPARQL 1.1 CONSTRUCT",
292                ApiCategory::QueryLanguage,
293                "0.1.0",
294                "SPARQL 1.1 CONSTRUCT queries producing new RDF graphs from existing data.",
295            )
296            .with_spec("https://www.w3.org/TR/sparql11-query/"),
297        );
298        r.register(
299            ApiStabilityMarker::stable(
300                "SPARQL 1.1 DESCRIBE",
301                ApiCategory::QueryLanguage,
302                "0.1.0",
303                "SPARQL 1.1 DESCRIBE queries returning RDF descriptions of resources.",
304            )
305            .with_spec("https://www.w3.org/TR/sparql11-query/"),
306        );
307        r.register(ApiStabilityMarker::stable("SPARQL 1.1 Update", ApiCategory::QueryLanguage, "0.1.0",
308            "Full SPARQL 1.1 Update support: INSERT DATA, DELETE DATA, INSERT/DELETE WHERE, LOAD, CLEAR, CREATE, DROP.")
309            .with_spec("https://www.w3.org/TR/sparql11-update/"));
310        r.register(ApiStabilityMarker::stable("SPARQL 1.1 Property Paths", ApiCategory::QueryLanguage, "0.1.0",
311            "SPARQL 1.1 property path expressions including sequence, alternative, inverse, kleene star, and kleene plus."));
312        r.register(ApiStabilityMarker::stable(
313            "SPARQL 1.1 Aggregates",
314            ApiCategory::QueryLanguage,
315            "0.1.0",
316            "SPARQL 1.1 aggregate functions: COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, SAMPLE.",
317        ));
318        r.register(
319            ApiStabilityMarker::stable(
320                "SPARQL 1.1 Federated Query (SERVICE)",
321                ApiCategory::QueryLanguage,
322                "0.1.0",
323                "SPARQL 1.1 SERVICE clause for federated queries across multiple SPARQL endpoints.",
324            )
325            .with_spec("https://www.w3.org/TR/sparql11-federated-query/"),
326        );
327        r.register(
328            ApiStabilityMarker::stable(
329                "SPARQL 1.2 RDF-star Expressions",
330                ApiCategory::RdfStar,
331                "0.1.0",
332                "SPARQL 1.2 triple expressions and annotation syntax for quoted triple patterns.",
333            )
334            .with_spec("https://www.w3.org/TR/sparql12-query/"),
335        );
336
337        // RDF Data Model
338        r.register(ApiStabilityMarker::stable("RDF 1.2 Data Model", ApiCategory::DataModel, "0.1.0",
339            "Complete RDF 1.2 data model: IRIs, blank nodes, literals, language-tagged strings, datatyped literals, and quoted triples.")
340            .with_spec("https://www.w3.org/TR/rdf12-concepts/"));
341        r.register(
342            ApiStabilityMarker::stable(
343                "Turtle Serialization",
344                ApiCategory::DataModel,
345                "0.1.0",
346                "Streaming Turtle 1.1 parser and writer with full namespace prefix support.",
347            )
348            .with_spec("https://www.w3.org/TR/turtle/"),
349        );
350        r.register(
351            ApiStabilityMarker::stable(
352                "N-Triples Serialization",
353                ApiCategory::DataModel,
354                "0.1.0",
355                "N-Triples parser and writer for simple line-oriented RDF serialization.",
356            )
357            .with_spec("https://www.w3.org/TR/n-triples/"),
358        );
359        r.register(
360            ApiStabilityMarker::stable(
361                "N-Quads Serialization",
362                ApiCategory::DataModel,
363                "0.1.0",
364                "N-Quads parser and writer for quad-based RDF dataset serialization.",
365            )
366            .with_spec("https://www.w3.org/TR/n-quads/"),
367        );
368        r.register(
369            ApiStabilityMarker::stable(
370                "TriG Serialization",
371                ApiCategory::DataModel,
372                "0.1.0",
373                "TriG parser and writer for named graph serialization.",
374            )
375            .with_spec("https://www.w3.org/TR/trig/"),
376        );
377        r.register(
378            ApiStabilityMarker::stable(
379                "RDF/XML Serialization",
380                ApiCategory::DataModel,
381                "0.1.0",
382                "RDF/XML parser and writer for legacy compatibility.",
383            )
384            .with_spec("https://www.w3.org/TR/rdf-syntax-grammar/"),
385        );
386        r.register(
387            ApiStabilityMarker::stable(
388                "JSON-LD 1.1 Serialization",
389                ApiCategory::DataModel,
390                "0.1.0",
391                "JSON-LD 1.1 parser and writer with context expansion and compaction.",
392            )
393            .with_spec("https://www.w3.org/TR/json-ld11/"),
394        );
395        r.register(ApiStabilityMarker::stable(
396            "Named Graphs",
397            ApiCategory::DataModel,
398            "0.1.0",
399            "Full quad store support with named graph CRUD operations and graph management.",
400        ));
401
402        // Reasoning
403        r.register(ApiStabilityMarker::stable("RDFS Inference", ApiCategory::Reasoning, "0.1.0",
404            "RDFS entailment regime including subClassOf, subPropertyOf, domain, range, and transitive closure.")
405            .with_spec("https://www.w3.org/TR/rdf-schema/"));
406        r.register(ApiStabilityMarker::stable("OWL 2 RL Reasoning", ApiCategory::Reasoning, "0.1.0",
407            "OWL 2 RL profile reasoning with forward-chaining rules for description logic reasoning over large datasets.")
408            .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_RL"));
409        r.register(ApiStabilityMarker::stable("OWL 2 EL Reasoning", ApiCategory::Reasoning, "0.1.0",
410            "OWL 2 EL profile reasoning optimized for large biomedical and enterprise ontologies.")
411            .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_EL"));
412        r.register(ApiStabilityMarker::beta("OWL 2 QL Reasoning", ApiCategory::Reasoning, "0.2.0",
413            "OWL 2 QL profile reasoning for query rewriting over relational sources. Conjunctive query rewriting is complete.")
414            .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_QL"));
415        r.register(
416            ApiStabilityMarker::stable(
417                "SWRL Rule Execution",
418                ApiCategory::Reasoning,
419                "0.2.0",
420                "Semantic Web Rule Language (SWRL) rule parsing and forward-chaining execution.",
421            )
422            .with_spec("https://www.w3.org/Submission/SWRL/"),
423        );
424
425        // GraphQL
426        r.register(ApiStabilityMarker::stable("GraphQL API", ApiCategory::GraphQuery, "0.1.0",
427            "Full GraphQL query execution over RDF datasets with automatic schema generation from RDF shapes."));
428        r.register(ApiStabilityMarker::stable("GraphQL Federation", ApiCategory::GraphQuery, "0.2.0",
429            "GraphQL Federation v2 support for composing distributed subgraphs into a unified supergraph."));
430        r.register(ApiStabilityMarker::beta(
431            "GraphQL Subscriptions",
432            ApiCategory::GraphQuery,
433            "0.2.0",
434            "GraphQL subscriptions over WebSocket with change tracking and subscription optimizer.",
435        ));
436        r.register(ApiStabilityMarker::beta("GraphQL Mutations", ApiCategory::GraphQuery, "0.2.0",
437            "GraphQL mutations mapped to SPARQL UPDATE operations for write-through to the RDF store."));
438
439        // GeoSPARQL
440        r.register(ApiStabilityMarker::stable("GeoSPARQL 1.1", ApiCategory::Geospatial, "0.1.0",
441            "GeoSPARQL 1.1 spatial query functions, geometry relations, and WKT/GeoJSON serialization.")
442            .with_spec("https://www.ogc.org/standard/geosparql/"));
443        r.register(ApiStabilityMarker::beta(
444            "GeoSPARQL 3D Geometry",
445            ApiCategory::Geospatial,
446            "0.2.0",
447            "3D geometry support (PolyhedralSurface, MultiSolid) for volumetric spatial queries.",
448        ));
449        r.register(ApiStabilityMarker::beta("GeoSPARQL Coordinate Reference Systems", ApiCategory::Geospatial, "0.2.0",
450            "CRS transformation support for queries involving data in multiple spatial reference systems."));
451        r.register(ApiStabilityMarker::beta(
452            "GeoSPARQL R-Tree Index",
453            ApiCategory::Geospatial,
454            "0.2.0",
455            "R-tree spatial index for accelerating GeoSPARQL geometric relation queries.",
456        ));
457
458        // Validation
459        r.register(ApiStabilityMarker::stable("SHACL Validation", ApiCategory::Validation, "0.1.0",
460            "Full SHACL 1.0 constraint validation including cardinality, value type, pattern, and SPARQL constraints.")
461            .with_spec("https://www.w3.org/TR/shacl/"));
462        r.register(ApiStabilityMarker::stable(
463            "SHACL-SPARQL Constraints",
464            ApiCategory::Validation,
465            "0.1.0",
466            "SHACL-SPARQL constraints allowing arbitrary SPARQL-based shape validation.",
467        ));
468        r.register(ApiStabilityMarker::stable("SAMM Aspect Model", ApiCategory::Validation, "0.1.0",
469            "SAMM (Semantic Aspect Meta Model) support for industrial asset modeling and validation."));
470        r.register(ApiStabilityMarker::beta("SHACL-AI Shape Learning", ApiCategory::Validation, "0.2.0",
471            "AI-powered automatic SHACL shape inference from example data using pattern mining and constraint learning."));
472
473        // Storage
474        r.register(ApiStabilityMarker::stable("In-Memory RDF Store", ApiCategory::Storage, "0.1.0",
475            "High-performance in-memory RDF store with multi-index support (SPO/POS/OSP) and ACID transactions."));
476        r.register(ApiStabilityMarker::stable("TDB Persistent Storage", ApiCategory::Storage, "0.1.0",
477            "TDB-compatible disk-based RDF storage with B-tree indices, WAL journal, and crash recovery."));
478        r.register(ApiStabilityMarker::stable("RocksDB Backend", ApiCategory::Storage, "0.1.0",
479            "RocksDB-backed persistent storage for high-throughput write workloads with SST compaction."));
480        r.register(ApiStabilityMarker::beta("Distributed Cluster Storage", ApiCategory::Storage, "0.2.0",
481            "Raft-based distributed storage cluster with automatic sharding, replication, and leader election."));
482        r.register(ApiStabilityMarker::beta("Full-Text Search Index", ApiCategory::Storage, "0.2.0",
483            "Tantivy-powered full-text search index for literal values with BM25 ranking and language filtering."));
484        r.register(ApiStabilityMarker::beta("Temporal RDF Storage", ApiCategory::Storage, "0.2.0",
485            "Versioned triple storage with time-travel queries, changelog, and bitemporal data support."));
486
487        // Streaming / Federation
488        r.register(ApiStabilityMarker::beta("RDF Streaming Processing", ApiCategory::Streaming, "0.1.0",
489            "Real-time RDF stream processing with windowed aggregations and complex event patterns."));
490        r.register(ApiStabilityMarker::beta("Kafka Integration", ApiCategory::Streaming, "0.1.0",
491            "Apache Kafka consumer/producer integration for ingesting RDF triples from event streams."));
492        r.register(ApiStabilityMarker::beta(
493            "NATS Integration",
494            ApiCategory::Streaming,
495            "0.1.0",
496            "NATS.io message broker integration for lightweight RDF event streaming.",
497        ));
498        r.register(ApiStabilityMarker::beta("Federated SPARQL Query", ApiCategory::Federation, "0.2.0",
499            "Distributed SPARQL query execution across multiple OxiRS endpoints with cost-based join reordering."));
500        r.register(ApiStabilityMarker::beta("Stream Fault Tolerance", ApiCategory::Streaming, "0.2.0",
501            "Bulkhead isolation, supervisor trees, and checkpoint recovery for streaming pipeline resilience."));
502
503        // Industrial
504        r.register(ApiStabilityMarker::beta("Modbus TCP/RTU", ApiCategory::Industrial, "0.1.0",
505            "Modbus TCP and RTU protocol client with RDF mapping for industrial sensor data ingestion."));
506        r.register(ApiStabilityMarker::beta(
507            "Modbus ASCII Protocol",
508            ApiCategory::Industrial,
509            "0.2.0",
510            "Modbus ASCII framing with LRC checksum for legacy industrial device connectivity.",
511        ));
512        r.register(ApiStabilityMarker::beta(
513            "Modbus TLS Client",
514            ApiCategory::Industrial,
515            "0.2.0",
516            "Modbus Secure (TLS over IANA port 802) for encrypted industrial communications.",
517        ));
518        r.register(ApiStabilityMarker::beta("CANbus/J1939 Integration", ApiCategory::Industrial, "0.1.0",
519            "CANbus and J1939 protocol support with automatic RDF triple generation from CAN frames."));
520        r.register(ApiStabilityMarker::beta("UDS ISO 14229 Diagnostics", ApiCategory::Industrial, "0.2.0",
521            "Unified Diagnostic Services (ISO 14229) protocol client for automotive ECU diagnostics."));
522        r.register(ApiStabilityMarker::beta(
523            "CANopen DS-301",
524            ApiCategory::Industrial,
525            "0.2.0",
526            "CANopen DS-301 protocol support with NMT, SDO, PDO, and object dictionary management.",
527        ));
528
529        // AI
530        r.register(ApiStabilityMarker::beta("Vector Similarity Search", ApiCategory::ArtificialIntelligence, "0.1.0",
531            "HNSW-based approximate nearest neighbour search for semantic similarity queries over RDF entity embeddings."));
532        r.register(ApiStabilityMarker::beta("Knowledge Graph Embeddings", ApiCategory::ArtificialIntelligence, "0.1.0",
533            "TransE, DistMult, ComplEx, and RotatE knowledge graph embedding models for entity and relation representation."));
534        r.register(ApiStabilityMarker::beta("GraphRAG Retrieval", ApiCategory::ArtificialIntelligence, "0.2.0",
535            "Graph-Retrieval-Augmented Generation (GraphRAG) combining SPARQL traversal with LLM reasoning for QA."));
536        r.register(ApiStabilityMarker::beta("Graph Neural Networks", ApiCategory::ArtificialIntelligence, "0.2.0",
537            "GraphSAGE and Graph Attention Network (GAT) implementations for inductive knowledge graph representation learning."));
538        r.register(ApiStabilityMarker::beta("Conversational AI (RAG Chat)", ApiCategory::ArtificialIntelligence, "0.1.0",
539            "Multi-turn conversational AI with retrieval-augmented generation over RDF knowledge graphs."));
540        r.register(ApiStabilityMarker::experimental("Physics Simulation", ApiCategory::PhysicsSimulation, "0.2.0",
541            "RDF-backed physics simulation with digital twin support (DTDL v2), conservation laws, and predictive maintenance."));
542        r.register(ApiStabilityMarker::experimental(
543            "Digital Twin Framework",
544            ApiCategory::PhysicsSimulation,
545            "0.2.0",
546            "DTDL v2 digital twin modeling with synchronisation reports and RDF integration.",
547        ));
548        r.register(ApiStabilityMarker::experimental("Predictive Maintenance", ApiCategory::PhysicsSimulation, "0.2.0",
549            "ML-based predictive maintenance using anomaly detection and remaining useful life estimation."));
550
551        // Time-Series
552        r.register(ApiStabilityMarker::beta("Time-Series Database", ApiCategory::TimeSeries, "0.1.0",
553            "High-performance time-series storage with columnar compression, range queries, and downsampling."));
554        r.register(ApiStabilityMarker::beta("Anomaly Detection", ApiCategory::TimeSeries, "0.2.0",
555            "Statistical anomaly detection using Z-score, IQR, EWMA, and Isolation Forest algorithms."));
556        r.register(ApiStabilityMarker::beta(
557            "Time-Series Forecasting",
558            ApiCategory::TimeSeries,
559            "0.2.0",
560            "Holt-Winters exponential smoothing for time-series forecasting and trend analysis.",
561        ));
562        r.register(ApiStabilityMarker::beta("Prometheus Remote Write", ApiCategory::TimeSeries, "0.2.0",
563            "Prometheus remote write protocol support for ingesting metrics into OxiRS time-series storage."));
564
565        // Security
566        r.register(ApiStabilityMarker::beta("DID (Decentralised Identifiers)", ApiCategory::Security, "0.2.0",
567            "W3C Decentralised Identifier support (did:key, did:web, did:ion, did:pkh, did:ethr) with key management.")
568            .with_spec("https://www.w3.org/TR/did-core/"));
569        r.register(ApiStabilityMarker::beta("W3C Verifiable Credentials", ApiCategory::Security, "0.2.0",
570            "W3C Verifiable Credentials Data Model 2.0 with JSON-LD proof signatures and verification.")
571            .with_spec("https://www.w3.org/TR/vc-data-model-2.0/"));
572        r.register(ApiStabilityMarker::experimental(
573            "Zero-Knowledge Proofs",
574            ApiCategory::Security,
575            "0.2.0",
576            "ZKP-based credential presentations for privacy-preserving identity verification.",
577        ));
578        r.register(ApiStabilityMarker::experimental(
579            "DID Key Rotation",
580            ApiCategory::Security,
581            "0.2.0",
582            "Automated cryptographic key rotation with DID document update propagation.",
583        ));
584
585        // Server
586        r.register(ApiStabilityMarker::stable("SPARQL 1.1 HTTP Protocol", ApiCategory::Server, "0.1.0",
587            "W3C SPARQL 1.1 HTTP protocol endpoint with GET and POST query support, content negotiation, and streaming results.")
588            .with_spec("https://www.w3.org/TR/sparql11-protocol/"));
589        r.register(ApiStabilityMarker::stable("Fuseki-Compatible REST API", ApiCategory::Server, "0.1.0",
590            "Apache Jena Fuseki-compatible REST API for dataset management, upload, and administration."));
591        r.register(ApiStabilityMarker::stable("GraphQL HTTP Server", ApiCategory::Server, "0.1.0",
592            "GraphQL-over-HTTP server with multipart upload, introspection, and schema-first API design."));
593        r.register(ApiStabilityMarker::beta(
594            "Multi-tenancy",
595            ApiCategory::Server,
596            "0.2.0",
597            "Dataset-level multi-tenancy with isolated namespaces and per-tenant resource quotas.",
598        ));
599
600        // WebAssembly
601        r.register(ApiStabilityMarker::beta(
602            "WASM RDF Store",
603            ApiCategory::WebAssembly,
604            "0.2.0",
605            "Full OxiRS RDF store compiled to WebAssembly for in-browser and edge deployment.",
606        ));
607        r.register(ApiStabilityMarker::beta(
608            "WASM SPARQL Engine",
609            ApiCategory::WebAssembly,
610            "0.2.0",
611            "SPARQL query execution in WebAssembly with zero native dependencies.",
612        ));
613        r.register(ApiStabilityMarker::beta("WASM SPARQL UPDATE", ApiCategory::WebAssembly, "0.2.0",
614            "SPARQL UPDATE operations available in the WASM build for client-side data modification."));
615
616        // Quantum
617        r.register(ApiStabilityMarker::experimental("Quantum Graph Optimization", ApiCategory::Quantum, "0.2.0",
618            "Quantum-inspired optimization algorithms for SPARQL query planning over large knowledge graphs."));
619        r.register(ApiStabilityMarker::experimental("Quantum SPARQL Sampling", ApiCategory::Quantum, "0.2.0",
620            "Quantum amplitude estimation for approximate SPARQL aggregate queries on large datasets."));
621
622        // Tooling
623        r.register(ApiStabilityMarker::stable("OxiRS CLI Tool", ApiCategory::Tooling, "0.1.0",
624            "Command-line interface for SPARQL queries, dataset management, benchmarking, and graph analytics."));
625        r.register(ApiStabilityMarker::stable("SPARQL Query Profiler", ApiCategory::Tooling, "0.2.0",
626            "Query execution profiler with operator-level timing, memory usage, and optimization suggestions."));
627        r.register(ApiStabilityMarker::beta("Jena Parity Checker", ApiCategory::Tooling, "0.2.0",
628            "Automated verification tool comparing OxiRS feature coverage against Apache Jena baseline."));
629        r.register(ApiStabilityMarker::beta("API Stability Report", ApiCategory::Tooling, "0.2.0",
630            "Automated API stability report generation tracking stability commitments across OxiRS releases."));
631
632        r
633    }
634
635    /// Returns all registered API stability markers.
636    pub fn all_apis(&self) -> &[ApiStabilityMarker] {
637        &self.markers
638    }
639
640    /// Returns all APIs at the Stable level.
641    pub fn stable_apis(&self) -> Vec<&ApiStabilityMarker> {
642        self.markers.iter().filter(|m| m.is_stable()).collect()
643    }
644
645    /// Returns all APIs at the Beta level.
646    pub fn beta_apis(&self) -> Vec<&ApiStabilityMarker> {
647        self.markers.iter().filter(|m| m.is_beta()).collect()
648    }
649
650    /// Returns all APIs at the Experimental level.
651    pub fn experimental_apis(&self) -> Vec<&ApiStabilityMarker> {
652        self.markers
653            .iter()
654            .filter(|m| m.is_experimental())
655            .collect()
656    }
657
658    /// Returns all Deprecated APIs.
659    pub fn deprecated_apis(&self) -> Vec<&ApiStabilityMarker> {
660        self.markers.iter().filter(|m| m.is_deprecated()).collect()
661    }
662
663    /// Returns all APIs in a given category.
664    pub fn apis_by_category(&self, category: &ApiCategory) -> Vec<&ApiStabilityMarker> {
665        self.markers
666            .iter()
667            .filter(|m| &m.category == category)
668            .collect()
669    }
670
671    /// Returns all APIs that are production-ready (Stable, Beta, or Deprecated).
672    pub fn production_ready_apis(&self) -> Vec<&ApiStabilityMarker> {
673        self.markers
674            .iter()
675            .filter(|m| m.level.is_production_ready())
676            .collect()
677    }
678
679    /// Computes summary statistics for this registry.
680    pub fn summary(&self) -> RegistrySummary {
681        RegistrySummary {
682            total: self.markers.len(),
683            stable_count: self.stable_apis().len(),
684            beta_count: self.beta_apis().len(),
685            experimental_count: self.experimental_apis().len(),
686            deprecated_count: self.deprecated_apis().len(),
687        }
688    }
689
690    /// Generates a comprehensive Markdown stability report.
691    pub fn generate_report(&self) -> String {
692        let summary = self.summary();
693        let mut report = String::with_capacity(8192);
694
695        report.push_str("# OxiRS API Stability Report\n\n");
696        report.push_str("> Auto-generated from the OxiRS StabilityRegistry.\n\n");
697        report.push_str("## Summary\n\n");
698        report.push_str("| Metric | Value |\n|--------|-------|\n");
699        report.push_str(&format!("| Total APIs tracked | {} |\n", summary.total));
700        report.push_str(&format!(
701            "| Stable | {} ({:.0}%) |\n",
702            summary.stable_count,
703            summary.stable_percentage()
704        ));
705        report.push_str(&format!("| Beta | {} |\n", summary.beta_count));
706        report.push_str(&format!(
707            "| Experimental | {} |\n",
708            summary.experimental_count
709        ));
710        report.push_str(&format!("| Deprecated | {} |\n", summary.deprecated_count));
711        report.push_str(&format!(
712            "| Production-ready (Stable+Beta) | {} ({:.0}%) |\n\n",
713            summary.stable_count + summary.beta_count,
714            (summary.stable_count + summary.beta_count) as f64 / summary.total.max(1) as f64
715                * 100.0
716        ));
717
718        let categories = [
719            ApiCategory::QueryLanguage,
720            ApiCategory::DataModel,
721            ApiCategory::RdfStar,
722            ApiCategory::Reasoning,
723            ApiCategory::GraphQuery,
724            ApiCategory::Geospatial,
725            ApiCategory::Validation,
726            ApiCategory::Storage,
727            ApiCategory::Streaming,
728            ApiCategory::Federation,
729            ApiCategory::Industrial,
730            ApiCategory::ArtificialIntelligence,
731            ApiCategory::TimeSeries,
732            ApiCategory::PhysicsSimulation,
733            ApiCategory::Security,
734            ApiCategory::Server,
735            ApiCategory::WebAssembly,
736            ApiCategory::Quantum,
737            ApiCategory::Tooling,
738        ];
739
740        report.push_str("## API Details by Category\n\n");
741        for category in &categories {
742            let apis = self.apis_by_category(category);
743            if apis.is_empty() {
744                continue;
745            }
746            report.push_str(&format!("### {}\n\n", category.label()));
747            report.push_str("| Feature | Status | Since | Description |\n");
748            report.push_str("|---------|--------|-------|-------------|\n");
749            for api in &apis {
750                let desc = if api.description.len() > 80 {
751                    format!("{}...", &api.description[..77])
752                } else {
753                    api.description.to_string()
754                };
755                report.push_str(&format!(
756                    "| {} | {} | {} | {} |\n",
757                    api.feature,
758                    api.level.indicator(),
759                    api.since,
760                    desc
761                ));
762            }
763            report.push('\n');
764        }
765
766        report.push_str("## Stability Level Definitions\n\n");
767        report.push_str("| Level | Guarantee |\n|-------|----------|\n");
768        report.push_str("| [STABLE] | Backward compatible throughout 1.x.y |\n");
769        report.push_str("| [BETA] | Stable but may change in minor releases |\n");
770        report.push_str("| [EXPERIMENTAL] | May change or be removed in any release |\n");
771        report.push_str("| [DEPRECATED] | Scheduled for removal in next major version |\n");
772
773        report
774    }
775
776    /// Validates all markers for non-empty required fields.
777    pub fn validate(&self) -> Vec<String> {
778        let mut errors = Vec::new();
779        for (i, marker) in self.markers.iter().enumerate() {
780            if marker.feature.is_empty() {
781                errors.push(format!("Marker #{i}: feature name is empty"));
782            }
783            if marker.description.is_empty() {
784                errors.push(format!(
785                    "Marker #{}: '{}' has empty description",
786                    i, marker.feature
787                ));
788            }
789            if marker.since.is_empty() {
790                errors.push(format!(
791                    "Marker #{}: '{}' has empty 'since' version",
792                    i, marker.feature
793                ));
794            }
795        }
796        errors
797    }
798}
799
800impl Default for StabilityRegistry {
801    fn default() -> Self {
802        Self::new()
803    }
804}
805
806#[cfg(test)]
807mod tests {
808    use super::*;
809    use std::collections::HashSet;
810
811    fn registry() -> StabilityRegistry {
812        StabilityRegistry::oxirs_v1_stability()
813    }
814
815    #[test]
816    fn test_registry_is_non_empty() {
817        assert!(!registry().all_apis().is_empty());
818    }
819
820    #[test]
821    fn test_registry_has_minimum_api_count() {
822        assert!(
823            registry().all_apis().len() >= 40,
824            "Registry should track at least 40 APIs"
825        );
826    }
827
828    #[test]
829    fn test_registry_validates_cleanly() {
830        let errors = registry().validate();
831        assert!(errors.is_empty(), "Validation errors: {:?}", errors);
832    }
833
834    #[test]
835    fn test_stable_apis_non_empty() {
836        assert!(!registry().stable_apis().is_empty());
837    }
838
839    #[test]
840    fn test_beta_apis_non_empty() {
841        assert!(!registry().beta_apis().is_empty());
842    }
843
844    #[test]
845    fn test_experimental_apis_non_empty() {
846        assert!(!registry().experimental_apis().is_empty());
847    }
848
849    #[test]
850    fn test_deprecated_apis_empty_by_default() {
851        assert!(registry().deprecated_apis().is_empty());
852    }
853
854    #[test]
855    fn test_stable_apis_are_all_stable() {
856        for api in registry().stable_apis() {
857            assert!(
858                matches!(api.level, StabilityLevel::Stable),
859                "{} should be Stable",
860                api.feature
861            );
862        }
863    }
864
865    #[test]
866    fn test_beta_apis_are_all_beta() {
867        for api in registry().beta_apis() {
868            assert!(
869                matches!(api.level, StabilityLevel::Beta),
870                "{} should be Beta",
871                api.feature
872            );
873        }
874    }
875
876    #[test]
877    fn test_experimental_apis_are_all_experimental() {
878        for api in registry().experimental_apis() {
879            assert!(
880                matches!(api.level, StabilityLevel::Experimental),
881                "{} should be Experimental",
882                api.feature
883            );
884        }
885    }
886
887    #[test]
888    fn test_counts_sum_to_total() {
889        let reg = registry();
890        let s = reg.summary();
891        assert_eq!(
892            s.stable_count + s.beta_count + s.experimental_count + s.deprecated_count,
893            s.total
894        );
895    }
896
897    #[test]
898    fn test_query_language_apis_exist() {
899        assert!(!registry()
900            .apis_by_category(&ApiCategory::QueryLanguage)
901            .is_empty());
902    }
903
904    #[test]
905    fn test_data_model_apis_exist() {
906        assert!(!registry()
907            .apis_by_category(&ApiCategory::DataModel)
908            .is_empty());
909    }
910
911    #[test]
912    fn test_reasoning_apis_exist() {
913        assert!(!registry()
914            .apis_by_category(&ApiCategory::Reasoning)
915            .is_empty());
916    }
917
918    #[test]
919    fn test_ai_apis_exist() {
920        assert!(!registry()
921            .apis_by_category(&ApiCategory::ArtificialIntelligence)
922            .is_empty());
923    }
924
925    #[test]
926    fn test_security_apis_exist() {
927        assert!(!registry()
928            .apis_by_category(&ApiCategory::Security)
929            .is_empty());
930    }
931
932    #[test]
933    fn test_tooling_apis_exist() {
934        assert!(!registry()
935            .apis_by_category(&ApiCategory::Tooling)
936            .is_empty());
937    }
938
939    #[test]
940    fn test_geospatial_apis_exist() {
941        assert!(!registry()
942            .apis_by_category(&ApiCategory::Geospatial)
943            .is_empty());
944    }
945
946    #[test]
947    fn test_storage_apis_exist() {
948        assert!(!registry()
949            .apis_by_category(&ApiCategory::Storage)
950            .is_empty());
951    }
952
953    #[test]
954    fn test_streaming_apis_exist() {
955        assert!(!registry()
956            .apis_by_category(&ApiCategory::Streaming)
957            .is_empty());
958    }
959
960    #[test]
961    fn test_industrial_apis_exist() {
962        assert!(!registry()
963            .apis_by_category(&ApiCategory::Industrial)
964            .is_empty());
965    }
966
967    #[test]
968    fn test_server_apis_exist() {
969        assert!(!registry().apis_by_category(&ApiCategory::Server).is_empty());
970    }
971
972    #[test]
973    fn test_wasm_apis_exist() {
974        assert!(!registry()
975            .apis_by_category(&ApiCategory::WebAssembly)
976            .is_empty());
977    }
978
979    #[test]
980    fn test_quantum_apis_exist() {
981        assert!(!registry()
982            .apis_by_category(&ApiCategory::Quantum)
983            .is_empty());
984    }
985
986    #[test]
987    fn test_time_series_apis_exist() {
988        assert!(!registry()
989            .apis_by_category(&ApiCategory::TimeSeries)
990            .is_empty());
991    }
992
993    #[test]
994    fn test_physics_apis_exist() {
995        assert!(!registry()
996            .apis_by_category(&ApiCategory::PhysicsSimulation)
997            .is_empty());
998    }
999
1000    #[test]
1001    fn test_rdf_star_apis_exist() {
1002        assert!(!registry()
1003            .apis_by_category(&ApiCategory::RdfStar)
1004            .is_empty());
1005    }
1006
1007    #[test]
1008    fn test_federation_apis_exist() {
1009        assert!(!registry()
1010            .apis_by_category(&ApiCategory::Federation)
1011            .is_empty());
1012    }
1013
1014    #[test]
1015    fn test_sparql_11_select_is_stable() {
1016        let reg = registry();
1017        let api = reg
1018            .all_apis()
1019            .iter()
1020            .find(|m| m.feature == "SPARQL 1.1 SELECT");
1021        assert!(api.is_some(), "SPARQL 1.1 SELECT should be registered");
1022        assert!(api.expect("API should be registered").is_stable());
1023    }
1024
1025    #[test]
1026    fn test_rdf_data_model_is_stable() {
1027        let reg = registry();
1028        let api = reg
1029            .all_apis()
1030            .iter()
1031            .find(|m| m.feature == "RDF 1.2 Data Model");
1032        assert!(api.is_some());
1033        assert!(api.expect("API should be registered").is_stable());
1034    }
1035
1036    #[test]
1037    fn test_graphql_api_is_stable() {
1038        let reg = registry();
1039        let api = reg.all_apis().iter().find(|m| m.feature == "GraphQL API");
1040        assert!(api.is_some());
1041        assert!(api.expect("API should be registered").is_stable());
1042    }
1043
1044    #[test]
1045    fn test_geosparql_is_stable() {
1046        let reg = registry();
1047        let api = reg.all_apis().iter().find(|m| m.feature == "GeoSPARQL 1.1");
1048        assert!(api.is_some());
1049        assert!(api.expect("API should be registered").is_stable());
1050    }
1051
1052    #[test]
1053    fn test_shacl_is_stable() {
1054        let reg = registry();
1055        let api = reg
1056            .all_apis()
1057            .iter()
1058            .find(|m| m.feature == "SHACL Validation");
1059        assert!(api.is_some());
1060        assert!(api.expect("API should be registered").is_stable());
1061    }
1062
1063    #[test]
1064    fn test_time_series_db_is_beta() {
1065        let reg = registry();
1066        let api = reg
1067            .all_apis()
1068            .iter()
1069            .find(|m| m.feature == "Time-Series Database");
1070        assert!(api.is_some());
1071        assert!(api.expect("API should be registered").is_beta());
1072    }
1073
1074    #[test]
1075    fn test_graphrag_is_beta() {
1076        let reg = registry();
1077        let api = reg
1078            .all_apis()
1079            .iter()
1080            .find(|m| m.feature == "GraphRAG Retrieval");
1081        assert!(api.is_some());
1082        assert!(api.expect("API should be registered").is_beta());
1083    }
1084
1085    #[test]
1086    fn test_physics_simulation_is_experimental() {
1087        let reg = registry();
1088        let api = reg
1089            .all_apis()
1090            .iter()
1091            .find(|m| m.feature == "Physics Simulation");
1092        assert!(api.is_some());
1093        assert!(api.expect("API should be registered").is_experimental());
1094    }
1095
1096    #[test]
1097    fn test_quantum_is_experimental() {
1098        let reg = registry();
1099        let api = reg
1100            .all_apis()
1101            .iter()
1102            .find(|m| m.feature == "Quantum Graph Optimization");
1103        assert!(api.is_some());
1104        assert!(api.expect("API should be registered").is_experimental());
1105    }
1106
1107    #[test]
1108    fn test_stable_percentage_in_range() {
1109        let s = registry().summary();
1110        assert!(s.stable_percentage() > 0.0 && s.stable_percentage() <= 100.0);
1111    }
1112
1113    #[test]
1114    fn test_production_ready_percentage_in_range() {
1115        let s = registry().summary();
1116        assert!(s.production_ready_percentage() > 0.0 && s.production_ready_percentage() <= 100.0);
1117    }
1118
1119    #[test]
1120    fn test_production_ready_ge_stable_percentage() {
1121        let s = registry().summary();
1122        assert!(s.production_ready_percentage() >= s.stable_percentage());
1123    }
1124
1125    #[test]
1126    fn test_generate_report_non_empty() {
1127        assert!(!registry().generate_report().is_empty());
1128    }
1129
1130    #[test]
1131    fn test_report_contains_summary_header() {
1132        assert!(registry().generate_report().contains("## Summary"));
1133    }
1134
1135    #[test]
1136    fn test_report_contains_stable_indicator() {
1137        assert!(registry().generate_report().contains("[STABLE]"));
1138    }
1139
1140    #[test]
1141    fn test_report_contains_beta_indicator() {
1142        assert!(registry().generate_report().contains("[BETA]"));
1143    }
1144
1145    #[test]
1146    fn test_report_contains_experimental_indicator() {
1147        assert!(registry().generate_report().contains("[EXPERIMENTAL]"));
1148    }
1149
1150    #[test]
1151    fn test_report_contains_api_details_header() {
1152        assert!(registry()
1153            .generate_report()
1154            .contains("## API Details by Category"));
1155    }
1156
1157    #[test]
1158    fn test_report_contains_sparql_select() {
1159        assert!(registry().generate_report().contains("SPARQL 1.1 SELECT"));
1160    }
1161
1162    #[test]
1163    fn test_stable_is_production_ready() {
1164        assert!(StabilityLevel::Stable.is_production_ready());
1165    }
1166
1167    #[test]
1168    fn test_beta_is_production_ready() {
1169        assert!(StabilityLevel::Beta.is_production_ready());
1170    }
1171
1172    #[test]
1173    fn test_experimental_is_not_production_ready() {
1174        assert!(!StabilityLevel::Experimental.is_production_ready());
1175    }
1176
1177    #[test]
1178    fn test_deprecated_is_production_ready() {
1179        let dep = StabilityLevel::Deprecated {
1180            since: "0.9.0",
1181            replacement: None,
1182        };
1183        assert!(dep.is_production_ready());
1184    }
1185
1186    #[test]
1187    fn test_stability_labels() {
1188        assert_eq!(StabilityLevel::Stable.label(), "Stable");
1189        assert_eq!(StabilityLevel::Beta.label(), "Beta");
1190        assert_eq!(StabilityLevel::Experimental.label(), "Experimental");
1191        let dep = StabilityLevel::Deprecated {
1192            since: "0.9.0",
1193            replacement: None,
1194        };
1195        assert_eq!(dep.label(), "Deprecated");
1196    }
1197
1198    #[test]
1199    fn test_stability_indicators() {
1200        assert_eq!(StabilityLevel::Stable.indicator(), "[STABLE]");
1201        assert_eq!(StabilityLevel::Beta.indicator(), "[BETA]");
1202        assert_eq!(StabilityLevel::Experimental.indicator(), "[EXPERIMENTAL]");
1203        let dep = StabilityLevel::Deprecated {
1204            since: "0.9.0",
1205            replacement: None,
1206        };
1207        assert_eq!(dep.indicator(), "[DEPRECATED]");
1208    }
1209
1210    #[test]
1211    fn test_marker_stable_builder() {
1212        let m = ApiStabilityMarker::stable("Test API", ApiCategory::Tooling, "1.0.0", "A test API");
1213        assert!(m.is_stable());
1214        assert_eq!(m.feature, "Test API");
1215        assert!(m.spec_url.is_none());
1216    }
1217
1218    #[test]
1219    fn test_marker_with_spec_url() {
1220        let m = ApiStabilityMarker::stable("Spec API", ApiCategory::QueryLanguage, "1.0.0", "desc")
1221            .with_spec("https://example.org/spec");
1222        assert_eq!(m.spec_url, Some("https://example.org/spec"));
1223    }
1224
1225    #[test]
1226    fn test_marker_beta_builder() {
1227        let m = ApiStabilityMarker::beta("Beta Feature", ApiCategory::Streaming, "0.5.0", "desc");
1228        assert!(m.is_beta());
1229        assert!(!m.is_stable());
1230    }
1231
1232    #[test]
1233    fn test_marker_experimental_builder() {
1234        let m =
1235            ApiStabilityMarker::experimental("Exp Feature", ApiCategory::Quantum, "0.9.0", "desc");
1236        assert!(m.is_experimental());
1237        assert!(!m.is_beta());
1238    }
1239
1240    #[test]
1241    fn test_marker_deprecated_builder() {
1242        let m = ApiStabilityMarker::deprecated(
1243            "Old Feature",
1244            ApiCategory::Tooling,
1245            "0.1.0",
1246            "0.9.0",
1247            Some("New Feature"),
1248            "desc",
1249        );
1250        assert!(m.is_deprecated());
1251        if let StabilityLevel::Deprecated { since, replacement } = &m.level {
1252            assert_eq!(*since, "0.9.0");
1253            assert_eq!(*replacement, Some("New Feature"));
1254        }
1255    }
1256
1257    #[test]
1258    fn test_feature_names_are_unique() {
1259        let reg = registry();
1260        let mut seen = HashSet::new();
1261        for api in reg.all_apis() {
1262            assert!(
1263                seen.insert(api.feature),
1264                "Duplicate feature name: '{}'",
1265                api.feature
1266            );
1267        }
1268    }
1269
1270    #[test]
1271    fn test_production_ready_apis_non_empty() {
1272        assert!(!registry().production_ready_apis().is_empty());
1273    }
1274
1275    #[test]
1276    fn test_production_ready_count_matches_summary() {
1277        let reg = registry();
1278        let s = reg.summary();
1279        let actual = reg.production_ready_apis().len();
1280        assert_eq!(actual, s.stable_count + s.beta_count + s.deprecated_count);
1281    }
1282
1283    #[test]
1284    fn test_default_registry_is_empty() {
1285        let reg = StabilityRegistry::default();
1286        assert_eq!(reg.all_apis().len(), 0);
1287    }
1288
1289    #[test]
1290    fn test_register_and_retrieve() {
1291        let mut reg = StabilityRegistry::new();
1292        reg.register(ApiStabilityMarker::stable(
1293            "Custom API",
1294            ApiCategory::Tooling,
1295            "1.0.0",
1296            "Custom",
1297        ));
1298        assert_eq!(reg.all_apis().len(), 1);
1299        assert_eq!(reg.stable_apis().len(), 1);
1300    }
1301
1302    #[test]
1303    fn test_all_markers_have_valid_since_version() {
1304        for api in registry().all_apis() {
1305            assert!(
1306                api.since.starts_with('0') || api.since.starts_with('1'),
1307                "'{}' has invalid since version: '{}'",
1308                api.feature,
1309                api.since
1310            );
1311        }
1312    }
1313
1314    #[test]
1315    fn test_stable_sparql_update_exists() {
1316        let reg = registry();
1317        let api = reg
1318            .all_apis()
1319            .iter()
1320            .find(|m| m.feature == "SPARQL 1.1 Update");
1321        assert!(api.is_some());
1322        assert!(api.expect("API should be registered").is_stable());
1323    }
1324
1325    #[test]
1326    fn test_owl2_rl_is_stable() {
1327        let reg = registry();
1328        let api = reg
1329            .all_apis()
1330            .iter()
1331            .find(|m| m.feature == "OWL 2 RL Reasoning");
1332        assert!(api.is_some());
1333        assert!(api.expect("API should be registered").is_stable());
1334    }
1335
1336    #[test]
1337    fn test_owl2_ql_is_beta() {
1338        let reg = registry();
1339        let api = reg
1340            .all_apis()
1341            .iter()
1342            .find(|m| m.feature == "OWL 2 QL Reasoning");
1343        assert!(api.is_some());
1344        assert!(api.expect("API should be registered").is_beta());
1345    }
1346
1347    #[test]
1348    fn test_category_label_non_empty() {
1349        let categories = [
1350            ApiCategory::QueryLanguage,
1351            ApiCategory::DataModel,
1352            ApiCategory::Reasoning,
1353            ApiCategory::GraphQuery,
1354            ApiCategory::Geospatial,
1355            ApiCategory::Validation,
1356            ApiCategory::Storage,
1357            ApiCategory::Streaming,
1358            ApiCategory::Industrial,
1359            ApiCategory::ArtificialIntelligence,
1360            ApiCategory::Security,
1361            ApiCategory::RdfStar,
1362            ApiCategory::Federation,
1363            ApiCategory::Server,
1364            ApiCategory::WebAssembly,
1365            ApiCategory::TimeSeries,
1366            ApiCategory::PhysicsSimulation,
1367            ApiCategory::Quantum,
1368            ApiCategory::Tooling,
1369        ];
1370        for cat in &categories {
1371            assert!(
1372                !cat.label().is_empty(),
1373                "Category label should not be empty"
1374            );
1375        }
1376    }
1377
1378    #[test]
1379    fn test_zkp_is_experimental() {
1380        let reg = registry();
1381        let api = reg
1382            .all_apis()
1383            .iter()
1384            .find(|m| m.feature == "Zero-Knowledge Proofs");
1385        assert!(api.is_some());
1386        assert!(api.expect("API should be registered").is_experimental());
1387    }
1388
1389    #[test]
1390    fn test_did_is_beta() {
1391        let reg = registry();
1392        let api = reg
1393            .all_apis()
1394            .iter()
1395            .find(|m| m.feature == "DID (Decentralised Identifiers)");
1396        assert!(api.is_some());
1397        assert!(api.expect("API should be registered").is_beta());
1398    }
1399
1400    #[test]
1401    fn test_turtle_serialization_has_spec_url() {
1402        let reg = registry();
1403        let api = reg
1404            .all_apis()
1405            .iter()
1406            .find(|m| m.feature == "Turtle Serialization");
1407        assert!(api.is_some());
1408        assert!(api.expect("API should be registered").spec_url.is_some());
1409    }
1410
1411    #[test]
1412    fn test_report_contains_stability_level_defs() {
1413        let report = registry().generate_report();
1414        assert!(report.contains("Stability Level Definitions"));
1415    }
1416}