Skip to main content

zeppelin_embed/
lib.rs

1//! Embedded hybrid-search engine primitives.
2//!
3//! # Durability
4//!
5//! [`lifecycle::durability::DurabilityMode`] defaults to `Derived`. The default
6//! issues no synchronization primitive at any commit tier; under it,
7//! [`lifecycle::durability::CommitTier`] is ignored entirely. An application
8//! crash or process kill does not lose acknowledged writes because the
9//! operating-system page cache retains them and writes them out afterward. A
10//! power cut or kernel panic can lose recently acknowledged writes. Recovery
11//! truncates the log at the first record whose checksum fails, leaving a
12//! structurally valid store with a missing recent tail rather than silently
13//! wrong data.
14//!
15//! `Derived` asserts that another store is authoritative and this store can be
16//! rebuilt from it. If this store is the only copy of the data, select
17//! [`lifecycle::durability::DurabilityMode::Durable`] explicitly.
18
19#![deny(
20    clippy::expect_used,
21    clippy::indexing_slicing,
22    clippy::panic,
23    clippy::todo,
24    clippy::unimplemented,
25    clippy::unwrap_used,
26    unsafe_op_in_unsafe_fn
27)]
28#![warn(missing_docs)]
29
30#[cfg(all(
31    feature = "graph-cypher",
32    not(any(
33        target_os = "macos",
34        all(target_os = "windows", target_arch = "x86_64")
35    ))
36))]
37compile_error!("graph-cypher supports macOS (arm64, x86_64) and Windows x64 only");
38
39#[cfg(feature = "allocation-audit")]
40mod allocation_audit;
41
42#[cfg(any(test, feature = "test-seams"))]
43#[doc(hidden)]
44pub mod adversarial_test_support;
45
46/// Fixed keyed fixture for integration tests of the Cypher writer seam.
47#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
48#[doc(hidden)]
49pub mod graph_structured_write_test_support {
50    use crate::lifecycle::{CancelToken, QueryControl, Store};
51    use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
52    use crate::property_graph::{
53        ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphGeneration, GraphName,
54        GraphProperty, GraphRevision, NodeId, PropertyData, PropertyValue,
55    };
56
57    /// Creates one application-keyed `:A {v: 1}` through the real writer.
58    pub fn create_keyed_node(store: &Store) -> Result<(NodeId, GraphGeneration), String> {
59        let mut labels = [GraphName::new("A").map_err(|e| format!("label: {e:?}"))?];
60        let mut properties = [GraphProperty::new(
61            GraphName::new("v").map_err(|e| format!("property name: {e:?}"))?,
62            PropertyValue::new(PropertyData::I64(1))
63                .map_err(|e| format!("property value: {e:?}"))?,
64        )];
65        let image = CanonicalContents::node(&mut labels, &mut properties, None, None)
66            .map_err(|e| format!("node image: {e:?}"))?;
67        let receipts = store
68            .apply_native_graph(
69                &[StructuredWrite {
70                    key: ApplicationKey::new(EntityKind::Node, "app", "ze214")
71                        .map_err(|e| format!("application key: {e:?}"))?,
72                    revision: GraphRevision::new(1).map_err(|e| format!("revision: {e:?}"))?,
73                    operation: StructuredOperation::Create,
74                    image: Some(WriteImage::Node(&image)),
75                }],
76                &QueryControl::Cancel(CancelToken::new()),
77            )
78            .map_err(|e| format!("structured create: {e:?}"))?;
79        if receipts.len() != 1 {
80            return Err("structured create did not return exactly one receipt".to_owned());
81        }
82        let receipt = receipts
83            .first()
84            .ok_or_else(|| "structured create returned no receipt".to_owned())?;
85        match receipt.entity {
86            EntityId::Node(node) => Ok((node, receipt.generation)),
87            EntityId::Relationship(_) => {
88                Err("structured create returned a relationship".to_owned())
89            }
90        }
91    }
92}
93
94#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
95#[doc(hidden)]
96pub mod graph_read_view_test_support {
97    /// One completed production boundary, emitted only after its exact direct
98    /// assertion and any paired clean control returned successfully.
99    #[derive(Clone, Debug, Eq, PartialEq)]
100    pub struct PathReceipt {
101        /// Registry key owned by the completed boundary.
102        pub key: &'static str,
103        /// Actual scheduled failures observed at that boundary.
104        pub fires: u64,
105        /// Same-seed clean executions observed after or beside the failure.
106        pub clean_controls: u64,
107    }
108
109    /// Primitive actual relationship observation for the independent ZE-129
110    /// oracle crate; this type contains no expected answer or comparator.
111    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
112    pub struct ObservedRelationship {
113        pub rel: u128,
114        pub source: u128,
115        pub target: u128,
116        pub relationship_type: u64,
117    }
118
119    /// Exact receipts and actual rows from one controlled probe execution.
120    #[derive(Clone, Debug, Eq, PartialEq)]
121    pub struct ActualProbeReport {
122        pub receipts: Vec<PathReceipt>,
123        pub relationships: Vec<ObservedRelationship>,
124    }
125    /// Confirms that the controlled fixture boundary is linked only into a
126    /// test-support build. Behavioral acceptance remains on the internal scoped
127    /// adapter so no installer or read capability becomes a shipping API.
128    pub fn controlled_boundary_is_available() -> bool {
129        std::mem::size_of::<crate::lifecycle::native_graph::NativeReadLease>() > 0
130            && std::mem::size_of::<crate::property_graph::storage::GraphReadView<'_, '_, '_, '_>>()
131                > 0
132    }
133
134    /// Runs the real controlled native adapter, fault, lifetime and release paths.
135    /// Panics are retained as test failures; the shipping graph feature has no
136    /// installer or fixture surface because this module requires test-support.
137    pub fn run_actual_probe(seed: u64) -> ActualProbeReport {
138        crate::lifecycle::native_graph::tests::run_adversarial_probe(seed)
139    }
140}
141
142#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
143#[doc(hidden)]
144pub mod graph_publication_test_support {
145    /// Runs only the directed production coordinator paths and returns observed receipts.
146    pub fn run_actual_probe(seed: u64) -> crate::graph_read_view_test_support::ActualProbeReport {
147        crate::lifecycle::native_graph::tests::publication::run_actual_probe(seed)
148    }
149}
150
151#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
152#[doc(hidden)]
153pub mod graph_identity_test_support {
154    use crate::graph_read_view_test_support::PathReceipt;
155
156    /// One observed adjacency row, bound to the node the question named.
157    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
158    pub struct ObservedAdjacency {
159        /// Node the expansion was bound to.
160        pub bound_node: u128,
161        /// Exact relationship type identity.
162        pub relationship_type: u64,
163        /// Exact relationship identity.
164        pub rel: u128,
165        /// Other endpoint of that row.
166        pub neighbor: u128,
167    }
168
169    /// Exact outcome of one keyed request. It holds no expected answer.
170    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
171    pub struct ObservedOutcome {
172        /// Key ordinal the request named.
173        pub key: u32,
174        /// Primitive rejection name, absent when the request was admitted.
175        pub rejection: Option<&'static str>,
176        /// Installed or replayed identity; zero on a rejection.
177        pub entity: u128,
178        /// Installed or replayed revision; zero on a rejection.
179        pub revision: u64,
180        /// Original generation for a replay, changed generation otherwise.
181        pub generation: u64,
182        /// Exact replay classification.
183        pub replayed: bool,
184    }
185
186    /// Complete visible rows of one watched expansion after one batch.
187    #[derive(Clone, Debug, Eq, PartialEq)]
188    pub struct ObservedWatch {
189        /// Batch index this answer follows.
190        pub batch: usize,
191        /// Published generation the reader observed.
192        pub generation: u64,
193        /// Key ordinal whose current incarnation was bound.
194        pub key: u32,
195        /// Whether the expansion was outgoing.
196        pub outgoing: bool,
197        /// Complete rows, in the order the engine returned them.
198        pub rows: Vec<ObservedAdjacency>,
199    }
200
201    /// Actual state observed from one real keyed identity script.
202    #[derive(Clone, Debug, Eq, PartialEq)]
203    pub struct IdentityState {
204        /// Ordered receipts and rejections of the script.
205        pub outcomes: Vec<ObservedOutcome>,
206        /// Watched expansions after every batch.
207        pub watched: Vec<ObservedWatch>,
208        /// Generation held by a reader admitted before the deletion.
209        pub retained_generation: u64,
210        /// Rows that retained reader still observes after the deletion,
211        /// the recreation and a real reclamation cycle.
212        pub retained_rows: Vec<ObservedAdjacency>,
213        /// Generation observed by a reader admitted after those changes.
214        pub fresh_generation: u64,
215        /// Rows that newly admitted reader observes.
216        pub fresh_rows: Vec<ObservedAdjacency>,
217    }
218
219    /// Receipts emitted only after each directed identity body completes.
220    #[derive(Clone, Debug, Eq, PartialEq)]
221    pub struct IdentityProbeReport {
222        /// Completed path receipts with actual fired and clean counts.
223        pub receipts: Vec<PathReceipt>,
224        /// Actual state observed from one real script.
225        pub state: IdentityState,
226    }
227
228    /// Runs the directed production identity paths used by ZE-36 acceptance.
229    pub fn run_actual_probe(seed: u64) -> IdentityProbeReport {
230        crate::lifecycle::native_graph::tests::run_identity_probe(seed)
231    }
232}
233
234#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
235#[doc(hidden)]
236pub mod graph_reclaim_test_support {
237    use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
238
239    /// Actual state observed after one real reclaim cycle and a reopen. It
240    /// holds no expected answer and no comparator.
241    #[derive(Clone, Debug, Eq, PartialEq)]
242    pub struct ReclaimState {
243        /// First created node identity.
244        pub first_node: u128,
245        /// Second created node identity.
246        pub second_node: u128,
247        /// OUT rows of the first node after the cycle and the reopen.
248        pub relationships: Vec<ObservedRelationship>,
249        /// Generation replayed for the original installing request.
250        pub replay_generation: u64,
251        /// Whether that request was classified as an exact replay.
252        pub replayed: bool,
253        /// Bytes physically unlinked by the cycle.
254        pub removed_bytes: u64,
255        /// Summed on-disk lengths of the files the cycle unlinked.
256        pub unlinked_file_bytes: u64,
257    }
258
259    /// Receipts emitted only after each directed reclamation body completes.
260    #[derive(Clone, Debug, Eq, PartialEq)]
261    pub struct ReclaimProbeReport {
262        /// Completed path receipts with actual fired and clean counts.
263        pub receipts: Vec<PathReceipt>,
264        /// Actual state after a real cycle.
265        pub state: ReclaimState,
266        /// Actual post-sweep key visibility and original install replay generation.
267        pub detach_sweep: (bool, u64),
268        /// Host byte limit, observed charge, successful steps and byte refusal.
269        pub host_budget: (u64, u64, u64, bool),
270    }
271
272    /// Narrow measured reader-race observations and serialized controls.
273    #[derive(Clone, Debug, Eq, PartialEq)]
274    pub struct RaceProbeReport {
275        /// Old generation, canonical bytes, and root/WAL unlink results.
276        pub observation: (u64, Vec<u8>, bool, bool),
277        /// Identical seed with serialized scheduling.
278        pub control: (u64, Vec<u8>, bool, bool),
279    }
280
281    /// Runs only ZE-176's same-coordinator reader races.
282    pub fn run_ze176_race_probe(seed: u64) -> RaceProbeReport {
283        crate::lifecycle::native_graph::tests::run_ze176_race_probe(seed)
284    }
285
286    /// Runs the directed production consolidation and reclamation paths used
287    /// by ZE-46 acceptance.
288    pub fn run_actual_probe(seed: u64) -> ReclaimProbeReport {
289        crate::lifecycle::native_graph::tests::run_reclaim_probe(seed)
290    }
291}
292
293#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
294#[doc(hidden)]
295pub mod graph_storage_fault_test_support {
296    use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
297    use crate::lifecycle::{OpenOptions, Store};
298    use crate::vfs::Vfs;
299    use std::path::Path;
300    use std::sync::Arc;
301
302    /// One keyed node observed in actual directory order. It holds no expected
303    /// answer and no comparator.
304    #[derive(Clone, Debug, Eq, PartialEq)]
305    pub struct ObservedKeyedNode {
306        /// Application-key bytes exactly as the fixture supplied them.
307        pub key: Vec<u8>,
308        /// Node identity the store resolved for that key.
309        pub node: u128,
310        /// Installed revision of that node record.
311        pub revision: u64,
312    }
313
314    /// Seeded per-class fault selection. The adversarial family owns the
315    /// schedule; this crate only executes the schedule it is handed.
316    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
317    pub struct StorageFaultSchedule {
318        /// Byte position, modulo the damaged region, of the class-1 bit flip.
319        pub artifact_ref_offset: u64,
320        /// Upper bound on the keyed nodes class 2 commits while it discovers
321        /// which chunk splits the node directory.
322        pub split_keys: u32,
323        /// Which pre-split key class 2 resolves first through the old root.
324        pub split_probe: u32,
325        /// How many class-2 artifact creates succeed before the fault fires.
326        pub split_skip: u32,
327        /// Which class-3 adjacency append fails after the OUT append succeeds.
328        pub out_in_append: u32,
329        /// Selects the class-4 root-replacement variant order.
330        pub root_variant: u8,
331        /// Seeded byte offsets and selection fixture order for classes 5-6.
332        pub qualification_seed: u64,
333    }
334
335    /// Measured reclamation or scoped-mapping fault cell and its clean control.
336    #[derive(Clone, Debug, Default, Eq, PartialEq)]
337    pub struct QualificationCell {
338        /// Fault boundary actually executed.
339        pub name: String,
340        /// Actual targeted injections and clean completions.
341        pub fires: u64,
342        pub controls: u64,
343        /// Explicit refused outcome, never a successful callback.
344        pub refusal: String,
345        /// Unauthorized deletes and changed protected files.
346        pub unauthorized_unlinks: u64,
347        pub changed_live_files: u64,
348        /// Pending authority survived, and all remaining targets resumed once.
349        pub intent_preserved: bool,
350        pub resumed: bool,
351        /// Reported bytes and independently summed physical unlink lengths.
352        pub removed_bytes: u64,
353        pub unlinked_bytes: u64,
354        /// Callback exposure on a refused mapping.
355        pub exposed_blocks: u64,
356        /// Slot fills at the scoped transition.
357        pub filled: usize,
358        /// Open attempts from the source and the independent VFS log.
359        pub opens: usize,
360        pub logged_opens: usize,
361        pub scoped_opens: usize,
362        /// Actual mark run/merge counts or folded manifest count.
363        pub runs: usize,
364        pub merges: usize,
365        pub folded: usize,
366        /// Successful publications making the count-only foreground leg due.
367        pub count_debt: u64,
368    }
369
370    /// Pack census measured before oldest-first selection, without random IDs.
371    #[derive(Clone, Debug, Eq, PartialEq)]
372    pub struct QualificationPack {
373        pub serial: u64,
374        pub bytes: u32,
375        pub live: u64,
376        pub pages: u32,
377        pub records: u32,
378        pub graph_live: bool,
379    }
380    /// Actual selection inputs and outputs, and same-seed clean identities.
381    #[derive(Clone, Debug, Default, Eq, PartialEq)]
382    pub struct QualificationSelection {
383        pub packs: Vec<QualificationPack>,
384        pub selected_serials: Vec<u64>,
385        pub clean_serials: Vec<u64>,
386        pub byte_limit: u64,
387        /// Direction and fixture group ordinal identify a range semantically.
388        pub ranges: Vec<(u8, usize, usize)>,
389        pub selected_ranges: Vec<(u8, usize)>,
390        pub clean_ranges: Vec<(u8, usize)>,
391        pub fixture_pending: Vec<usize>,
392    }
393
394    /// Per-commit source open counts compared with actual VFS open calls.
395    #[derive(Clone, Debug, Eq, PartialEq)]
396    pub struct QualificationCommit {
397        pub generation: u64,
398        pub opens: usize,
399        pub logged_opens: usize,
400        pub filled: usize,
401        pub scoped_opens: usize,
402    }
403
404    /// ZE-172 executions, separate from classes 1-4 for scoped binding runs.
405    #[derive(Clone, Debug, Default, Eq, PartialEq)]
406    pub struct QualificationReport {
407        pub cells: Vec<QualificationCell>,
408        pub selection: Option<QualificationSelection>,
409        pub commits: Vec<QualificationCommit>,
410        /// Mapped/private page decode deltas: two reads in one attempt, then a fresh one.
411        pub preparation_page_decodes: Option<[[u64; 3]; 2]>,
412    }
413
414    /// Execute real class-5 reclamation or class-6 scoped mapping cells.
415    pub fn run_qualification_probe(seed: u64, mapping: bool) -> QualificationReport {
416        crate::lifecycle::native_graph::tests::storage_faults::run_qualification_probe(
417            seed, mapping,
418        )
419    }
420
421    /// Actual state observed by one complete ZE-47 / ZE-172 execution.
422    #[derive(Clone, Debug, Eq, PartialEq)]
423    pub struct StorageFaultState {
424        /// Typed classification of every class-1 damaged-artifact refusal.
425        pub artifact_refusals: Vec<String>,
426        /// Nodes observed after each class-1 damage was reverted byte-exactly.
427        pub surviving_nodes: Vec<ObservedKeyedNode>,
428        /// Actual node-directory root level after the splitting commit
429        /// published, observed through a fresh lease.
430        pub split_level: u16,
431        /// Root level the retained pre-split lease still reports after that
432        /// same publication. A pre-split oracle must still read level zero.
433        pub retained_split_level: u16,
434        /// Keyed nodes committed before the splitting chunk, so the family
435        /// can model exactly the population both leases must answer.
436        pub pre_split_keys: u32,
437        /// Pre-split keys observed through a fresh post-split lease.
438        pub committed_keys: Vec<ObservedKeyedNode>,
439        /// The same keys resolved through the retained pre-split root.
440        pub old_root_keys: Vec<ObservedKeyedNode>,
441        /// Published generations observed before and after the class-2 faults
442        /// on the splitting commit, each through its own fresh lease.
443        pub split_generations: (u64, u64),
444        /// Xxh3-64 over the retained pre-split root page, before the faults
445        /// and after the splitting chunk published cleanly.
446        pub retained_root_digests: (u64, u64),
447        /// Generations before and after the WAL-envelope fault on the
448        /// splitting commit, the second one read through a reopen because
449        /// that refusal stops read admissions.
450        pub unsplit_generations: (u64, u64),
451        /// Node-directory root level that same reopen exposes. A split whose
452        /// WAL envelope never landed must not survive recovery.
453        pub unsplit_reopen_level: u16,
454        /// Typed classification of every class-2 refusal.
455        pub split_refusals: Vec<String>,
456        /// OUT rows of the class-3 relationship after a reopen.
457        pub out_rows: Vec<ObservedRelationship>,
458        /// IN rows of the same relationship after the same reopen.
459        pub in_rows: Vec<ObservedRelationship>,
460        /// Typed classification of every class-3 half-prepared refusal.
461        pub out_in_refusals: Vec<String>,
462        /// Typed classification of every class-4 root-replacement refusal.
463        pub root_refusals: Vec<String>,
464        /// Generation exposed by the class-4 reopen.
465        pub reopened_generation: u64,
466        /// Generation acknowledged before the class-4 fault.
467        pub previous_generation: u64,
468        /// Measured class-5/6 executions.
469        pub qualification: QualificationReport,
470    }
471
472    /// Receipts emitted only after each directed storage-fault body completes.
473    #[derive(Clone, Debug, Eq, PartialEq)]
474    pub struct StorageFaultProbeReport {
475        /// Completed path receipts with actual fired and clean counts.
476        pub receipts: Vec<PathReceipt>,
477        /// Actual state observed by the six fault classes.
478        pub state: StorageFaultState,
479    }
480
481    /// Runs the six directed ZE-47 / ZE-172 storage fault classes against a
482    /// real native store under a faulty VFS, following the supplied schedule.
483    pub fn run_actual_probe(seed: u64, schedule: StorageFaultSchedule) -> StorageFaultProbeReport {
484        crate::lifecycle::native_graph::tests::run_storage_fault_probe(seed, schedule)
485    }
486
487    /// Forwards the crate-private native-store constructor so an external
488    /// test crate can place its own [`Vfs`] under a real native store.
489    /// `lifecycle::native_graph` is `pub(crate)`, so `NativeGraphError` cannot
490    /// cross this seam; the refusal is rendered through its `Display`.
491    pub fn create_native_graph_with_infrastructure(
492        path: &Path,
493        options: OpenOptions,
494        vfs: Arc<dyn Vfs>,
495    ) -> Result<Store, String> {
496        Store::create_native_graph_with_infrastructure(
497            path,
498            options,
499            None,
500            vfs,
501            Arc::new(crate::lifecycle::SystemMonotonicClock),
502            &mut crate::property_graph::storage::allocation::OsEntropy,
503        )
504        .map_err(|error| error.to_string())
505    }
506
507    /// Forwards the crate-private native-store reopen constructor. See
508    /// [`create_native_graph_with_infrastructure`] for the error contract.
509    pub fn open_native_graph_with_infrastructure(
510        path: &Path,
511        options: OpenOptions,
512        vfs: Arc<dyn Vfs>,
513    ) -> Result<Store, String> {
514        Store::open_native_graph_with_infrastructure(
515            path,
516            options,
517            None,
518            vfs,
519            Arc::new(crate::lifecycle::SystemMonotonicClock),
520        )
521        .map_err(|error| error.to_string())
522    }
523}
524
525#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
526#[doc(hidden)]
527pub mod graph_recovery_test_support {
528    use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
529
530    /// Creates a runner fixture with explicit relationship deletion rules.
531    /// The constructor is crate-private; this test seam adds no production API.
532    pub fn create_with_relationship_rules(
533        path: &std::path::Path,
534        options: crate::lifecycle::OpenOptions,
535        document: Option<crate::epoch::EmbeddingTower>,
536        rules: crate::property_graph::catalog::RelationshipRules<'_>,
537    ) -> Result<crate::lifecycle::Store, String> {
538        crate::lifecycle::Store::create_native_graph_with_relationship_types(
539            path, options, document, rules,
540        )
541        .map_err(|error| error.to_string())
542    }
543
544    /// Selects explicit maintenance for the runner's acknowledgement ledger.
545    #[allow(clippy::result_large_err)]
546    pub fn configure_unified_runner(
547        store: &crate::lifecycle::Store,
548    ) -> Result<(), crate::property_graph::GraphStoreError> {
549        store
550            .set_native_graph_maintenance_policy(crate::property_graph::GraphMaintenancePolicy {
551                automatic: false,
552                ..Default::default()
553            })
554            .map_err(Into::into)
555    }
556
557    /// Dispatches runner requests to the same Store's production writer.
558    #[allow(clippy::result_large_err)]
559    pub fn apply_unified_batch(
560        store: &crate::lifecycle::Store,
561        documents: Option<&crate::ingest::IngestBatch>,
562        requests: &[crate::property_graph::staging::StructuredWrite<'_, '_>],
563        control: &crate::lifecycle::QueryControl,
564    ) -> Result<(u64, bool), crate::property_graph::GraphStoreError> {
565        let result = match documents {
566            Some(documents) => store.apply_native_mixed(documents, requests, control),
567            None => store.apply_native_graph(requests, control),
568        }
569        .map_err(crate::property_graph::GraphStoreError::from)?;
570        let changed = result.changed_generation();
571        Ok((
572            changed.unwrap_or(result.admitted_generation()).get(),
573            changed.is_some(),
574        ))
575    }
576
577    /// Observes whether this Store has an installed graph publication.
578    #[allow(clippy::result_large_err)]
579    pub fn unified_graph_enabled(
580        store: &crate::lifecycle::Store,
581    ) -> Result<bool, crate::property_graph::GraphStoreError> {
582        store.native_graph.is_installed().map_err(|error| {
583            crate::property_graph::GraphStoreError::from(
584                crate::lifecycle::native_graph::NativeGraphError::Store(error),
585            )
586        })
587    }
588
589    /// Drives the production mixed writer through the nonshipping fault facade.
590    pub fn apply_mixed_batch(
591        store: &crate::lifecycle::Store,
592        documents: &crate::ingest::IngestBatch,
593        key: &str,
594    ) -> Result<(crate::property_graph::NodeId, u64), String> {
595        use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
596        use crate::property_graph::{
597            ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphRevision,
598        };
599        let image =
600            CanonicalContents::node(&mut [], &mut [], None, None).map_err(|e| e.to_string())?;
601        let requests = [StructuredWrite {
602            key: ApplicationKey::new(EntityKind::Node, "mixed-probe", key)
603                .map_err(|e| e.to_string())?,
604            revision: GraphRevision::new(1).map_err(|e| e.to_string())?,
605            operation: StructuredOperation::Create,
606            image: Some(WriteImage::Node(&image)),
607        }];
608        let result = store
609            .apply_native_mixed(
610                documents,
611                &requests,
612                &crate::lifecycle::QueryControl::Cancel(crate::lifecycle::CancelToken::new()),
613            )
614            .map_err(|e| e.to_string())?;
615        let receipt = result.first().ok_or("mixed probe omitted node receipt")?;
616        let EntityId::Node(node) = receipt.entity else {
617            return Err("mixed probe returned relationship".into());
618        };
619        let generation = result
620            .changed_generation()
621            .ok_or("mixed probe did not commit")?;
622        Ok((node, generation.get()))
623    }
624
625    /// Observes a node through the native read admission after a mixed write.
626    pub fn mixed_node_present(
627        store: &crate::lifecycle::Store,
628        node: crate::property_graph::NodeId,
629    ) -> bool {
630        crate::lifecycle::native_graph::tests::observe_mixed_node(store, node)
631    }
632
633    /// Actual state observed after reopening one real mixed native commit.
634    #[derive(Clone, Debug, Eq, PartialEq)]
635    pub struct RecoveryState {
636        /// Actual persistent store identity.
637        pub store: u128,
638        /// Actual reopened graph generation.
639        pub generation: u64,
640        /// Actual reopened complete-envelope sequence.
641        pub sequence: u64,
642        /// First created node identity.
643        pub first_node: u128,
644        /// Second created node identity.
645        pub second_node: u128,
646        /// Actual relationship row observed in both directions.
647        pub relationship: ObservedRelationship,
648    }
649
650    /// Receipts emitted only after each directed recovery body completes.
651    #[derive(Clone, Debug, Eq, PartialEq)]
652    pub struct RecoveryProbeReport {
653        /// Completed path receipts with actual fired/clean counts.
654        pub receipts: Vec<PathReceipt>,
655        /// Actual reopened mixed state.
656        pub state: RecoveryState,
657    }
658
659    /// Exercises seal rotation with a graph record after the previous fold.
660    pub fn run_seal_rotation_probe() -> Vec<PathReceipt> {
661        crate::lifecycle::native_graph::tests::seal::run_rotation_probe()
662    }
663
664    /// Exercises late seal cancellation and failures of its durable cleanup.
665    pub fn run_seal_cancellation_probe() -> Vec<PathReceipt> {
666        crate::lifecycle::native_graph::tests::seal::run_cancellation_probe()
667    }
668
669    /// Exercises interrupted enable catalog creation and writable retry.
670    pub fn run_enable_retry_probe() -> Vec<PathReceipt> {
671        crate::lifecycle::native_graph::tests::recovery::run_enable_retry_probe()
672    }
673
674    /// Runs the nine directed production recovery paths used by ZE-40 acceptance.
675    pub fn run_actual_probe(seed: u64) -> RecoveryProbeReport {
676        crate::lifecycle::native_graph::tests::run_recovery_probe(seed)
677    }
678}
679
680/// Nonshipping public-facade observations for seeded ZE-41 qualification.
681#[cfg(all(feature = "graph-cypher", any(test, feature = "test-seams")))]
682pub mod graph_commit_recovery_test_support {
683    pub use crate::property_graph::commit_recovery_test_support::*;
684}
685
686/// Query diagnostics and health reporting.
687pub mod diag;
688/// Epoch identity and migration.
689pub mod epoch;
690/// Persisted-format framing, versions, and golden-fixture support.
691pub mod format;
692/// Full-text indexing and retrieval.
693pub mod fts;
694/// Hybrid result fusion.
695pub mod fusion;
696/// Per-segment vector graphs.
697pub mod graph;
698/// Ingest and mutation coordination.
699pub mod ingest;
700/// Runtime-dispatched compute kernels.
701pub mod kernels;
702/// Store lifecycle and memory accounting.
703pub mod lifecycle;
704/// Persistent manifest coordination.
705pub mod manifest;
706/// Columnar metadata and filters.
707pub mod meta;
708/// Query planning and selectivity decisions.
709pub mod planner;
710/// Native property-graph identity and values, independent of vector indexes.
711#[cfg(feature = "graph-cypher")]
712pub mod property_graph;
713/// Training-free vector quantization.
714pub mod quant;
715/// Exact vector scanning.
716pub mod scan;
717/// Immutable segment representation.
718pub mod segment;
719/// Operating-system integration wrappers.
720pub mod sys;
721/// Adaptive storage tiers.
722pub mod tier;
723/// Read-only end-to-end store verification.
724pub mod verify;
725/// Virtual filesystem and platform I/O.
726pub mod vfs;
727/// Write-ahead logging and durability.
728pub mod wal;
729
730#[cfg(test)]
731mod test_support;
732
733/// The current crate version.
734pub const VERSION: &str = env!("CARGO_PKG_VERSION");
735
736#[cfg(test)]
737#[allow(
738    clippy::expect_used,
739    clippy::indexing_slicing,
740    clippy::panic,
741    clippy::unwrap_used
742)]
743mod tests {
744    use rand::RngCore;
745
746    #[test]
747    #[cfg_attr(miri, ignore = "environment-dependent RNG is outside the Miri subset")]
748    fn seeded_rng_is_deterministic_per_name_and_env() {
749        let mut first =
750            crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
751        let mut second =
752            crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
753        let mut other_name = crate::test_support::seeded_rng("tests::another_test");
754
755        let first_draw = first.next_u64();
756        let second_draw = second.next_u64();
757        let other_name_draw = other_name.next_u64();
758
759        let environment_seed = std::env::var("ZE_TEST_SEED").unwrap_or_else(|_| String::from("0"));
760        let other_environment_seed = format!("{environment_seed}-different");
761        let mut other_env = crate::test_support::seeded_rng_from(
762            "tests::seeded_rng_is_deterministic_per_name_and_env",
763            &other_environment_seed,
764        );
765        let other_env_draw = other_env.next_u64();
766
767        assert_eq!(first_draw, second_draw);
768        assert_ne!(first_draw, other_name_draw);
769        assert_ne!(first_draw, other_env_draw);
770    }
771
772    #[test]
773    fn version_constant_is_current() {
774        assert_eq!(crate::VERSION, "0.8.0");
775    }
776}