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    }
411
412    /// Execute real class-5 reclamation or class-6 scoped mapping cells.
413    pub fn run_qualification_probe(seed: u64, mapping: bool) -> QualificationReport {
414        crate::lifecycle::native_graph::tests::storage_faults::run_qualification_probe(
415            seed, mapping,
416        )
417    }
418
419    /// Actual state observed by one complete ZE-47 / ZE-172 execution.
420    #[derive(Clone, Debug, Eq, PartialEq)]
421    pub struct StorageFaultState {
422        /// Typed classification of every class-1 damaged-artifact refusal.
423        pub artifact_refusals: Vec<String>,
424        /// Nodes observed after each class-1 damage was reverted byte-exactly.
425        pub surviving_nodes: Vec<ObservedKeyedNode>,
426        /// Actual node-directory root level after the splitting commit
427        /// published, observed through a fresh lease.
428        pub split_level: u16,
429        /// Root level the retained pre-split lease still reports after that
430        /// same publication. A pre-split oracle must still read level zero.
431        pub retained_split_level: u16,
432        /// Keyed nodes committed before the splitting chunk, so the family
433        /// can model exactly the population both leases must answer.
434        pub pre_split_keys: u32,
435        /// Pre-split keys observed through a fresh post-split lease.
436        pub committed_keys: Vec<ObservedKeyedNode>,
437        /// The same keys resolved through the retained pre-split root.
438        pub old_root_keys: Vec<ObservedKeyedNode>,
439        /// Published generations observed before and after the class-2 faults
440        /// on the splitting commit, each through its own fresh lease.
441        pub split_generations: (u64, u64),
442        /// Xxh3-64 over the retained pre-split root page, before the faults
443        /// and after the splitting chunk published cleanly.
444        pub retained_root_digests: (u64, u64),
445        /// Generations before and after the WAL-envelope fault on the
446        /// splitting commit, the second one read through a reopen because
447        /// that refusal stops read admissions.
448        pub unsplit_generations: (u64, u64),
449        /// Node-directory root level that same reopen exposes. A split whose
450        /// WAL envelope never landed must not survive recovery.
451        pub unsplit_reopen_level: u16,
452        /// Typed classification of every class-2 refusal.
453        pub split_refusals: Vec<String>,
454        /// OUT rows of the class-3 relationship after a reopen.
455        pub out_rows: Vec<ObservedRelationship>,
456        /// IN rows of the same relationship after the same reopen.
457        pub in_rows: Vec<ObservedRelationship>,
458        /// Typed classification of every class-3 half-prepared refusal.
459        pub out_in_refusals: Vec<String>,
460        /// Typed classification of every class-4 root-replacement refusal.
461        pub root_refusals: Vec<String>,
462        /// Generation exposed by the class-4 reopen.
463        pub reopened_generation: u64,
464        /// Generation acknowledged before the class-4 fault.
465        pub previous_generation: u64,
466        /// Measured class-5/6 executions.
467        pub qualification: QualificationReport,
468    }
469
470    /// Receipts emitted only after each directed storage-fault body completes.
471    #[derive(Clone, Debug, Eq, PartialEq)]
472    pub struct StorageFaultProbeReport {
473        /// Completed path receipts with actual fired and clean counts.
474        pub receipts: Vec<PathReceipt>,
475        /// Actual state observed by the six fault classes.
476        pub state: StorageFaultState,
477    }
478
479    /// Runs the six directed ZE-47 / ZE-172 storage fault classes against a
480    /// real native store under a faulty VFS, following the supplied schedule.
481    pub fn run_actual_probe(seed: u64, schedule: StorageFaultSchedule) -> StorageFaultProbeReport {
482        crate::lifecycle::native_graph::tests::run_storage_fault_probe(seed, schedule)
483    }
484
485    /// Forwards the crate-private native-store constructor so an external
486    /// test crate can place its own [`Vfs`] under a real native store.
487    /// `lifecycle::native_graph` is `pub(crate)`, so `NativeGraphError` cannot
488    /// cross this seam; the refusal is rendered through its `Display`.
489    pub fn create_native_graph_with_infrastructure(
490        path: &Path,
491        options: OpenOptions,
492        vfs: Arc<dyn Vfs>,
493    ) -> Result<Store, String> {
494        Store::create_native_graph_with_infrastructure(
495            path,
496            options,
497            None,
498            vfs,
499            Arc::new(crate::lifecycle::SystemMonotonicClock),
500            &mut crate::property_graph::storage::allocation::OsEntropy,
501        )
502        .map_err(|error| error.to_string())
503    }
504
505    /// Forwards the crate-private native-store reopen constructor. See
506    /// [`create_native_graph_with_infrastructure`] for the error contract.
507    pub fn open_native_graph_with_infrastructure(
508        path: &Path,
509        options: OpenOptions,
510        vfs: Arc<dyn Vfs>,
511    ) -> Result<Store, String> {
512        Store::open_native_graph_with_infrastructure(
513            path,
514            options,
515            None,
516            vfs,
517            Arc::new(crate::lifecycle::SystemMonotonicClock),
518        )
519        .map_err(|error| error.to_string())
520    }
521}
522
523#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
524#[doc(hidden)]
525pub mod graph_recovery_test_support {
526    use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
527
528    /// Creates a runner fixture with explicit relationship deletion rules.
529    /// The constructor is crate-private; this test seam adds no production API.
530    pub fn create_with_relationship_rules(
531        path: &std::path::Path,
532        options: crate::lifecycle::OpenOptions,
533        document: Option<crate::epoch::EmbeddingTower>,
534        rules: crate::property_graph::catalog::RelationshipRules<'_>,
535    ) -> Result<crate::lifecycle::Store, String> {
536        crate::lifecycle::Store::create_native_graph_with_relationship_types(
537            path, options, document, rules,
538        )
539        .map_err(|error| error.to_string())
540    }
541
542    /// Selects explicit maintenance for the runner's acknowledgement ledger.
543    #[allow(clippy::result_large_err)]
544    pub fn configure_unified_runner(
545        store: &crate::lifecycle::Store,
546    ) -> Result<(), crate::property_graph::GraphStoreError> {
547        store
548            .set_native_graph_maintenance_policy(crate::property_graph::GraphMaintenancePolicy {
549                automatic: false,
550                ..Default::default()
551            })
552            .map_err(Into::into)
553    }
554
555    /// Dispatches runner requests to the same Store's production writer.
556    #[allow(clippy::result_large_err)]
557    pub fn apply_unified_batch(
558        store: &crate::lifecycle::Store,
559        documents: Option<&crate::ingest::IngestBatch>,
560        requests: &[crate::property_graph::staging::StructuredWrite<'_, '_>],
561        control: &crate::lifecycle::QueryControl,
562    ) -> Result<(u64, bool), crate::property_graph::GraphStoreError> {
563        let result = match documents {
564            Some(documents) => store.apply_native_mixed(documents, requests, control),
565            None => store.apply_native_graph(requests, control),
566        }
567        .map_err(crate::property_graph::GraphStoreError::from)?;
568        let changed = result.changed_generation();
569        Ok((
570            changed.unwrap_or(result.admitted_generation()).get(),
571            changed.is_some(),
572        ))
573    }
574
575    /// Observes whether this Store has an installed graph publication.
576    #[allow(clippy::result_large_err)]
577    pub fn unified_graph_enabled(
578        store: &crate::lifecycle::Store,
579    ) -> Result<bool, crate::property_graph::GraphStoreError> {
580        store.native_graph.is_installed().map_err(|error| {
581            crate::property_graph::GraphStoreError::from(
582                crate::lifecycle::native_graph::NativeGraphError::Store(error),
583            )
584        })
585    }
586
587    /// Drives the production mixed writer through the nonshipping fault facade.
588    pub fn apply_mixed_batch(
589        store: &crate::lifecycle::Store,
590        documents: &crate::ingest::IngestBatch,
591        key: &str,
592    ) -> Result<(crate::property_graph::NodeId, u64), String> {
593        use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
594        use crate::property_graph::{
595            ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphRevision,
596        };
597        let image =
598            CanonicalContents::node(&mut [], &mut [], None, None).map_err(|e| e.to_string())?;
599        let requests = [StructuredWrite {
600            key: ApplicationKey::new(EntityKind::Node, "mixed-probe", key)
601                .map_err(|e| e.to_string())?,
602            revision: GraphRevision::new(1).map_err(|e| e.to_string())?,
603            operation: StructuredOperation::Create,
604            image: Some(WriteImage::Node(&image)),
605        }];
606        let result = store
607            .apply_native_mixed(
608                documents,
609                &requests,
610                &crate::lifecycle::QueryControl::Cancel(crate::lifecycle::CancelToken::new()),
611            )
612            .map_err(|e| e.to_string())?;
613        let receipt = result.first().ok_or("mixed probe omitted node receipt")?;
614        let EntityId::Node(node) = receipt.entity else {
615            return Err("mixed probe returned relationship".into());
616        };
617        let generation = result
618            .changed_generation()
619            .ok_or("mixed probe did not commit")?;
620        Ok((node, generation.get()))
621    }
622
623    /// Observes a node through the native read admission after a mixed write.
624    pub fn mixed_node_present(
625        store: &crate::lifecycle::Store,
626        node: crate::property_graph::NodeId,
627    ) -> bool {
628        crate::lifecycle::native_graph::tests::observe_mixed_node(store, node)
629    }
630
631    /// Actual state observed after reopening one real mixed native commit.
632    #[derive(Clone, Debug, Eq, PartialEq)]
633    pub struct RecoveryState {
634        /// Actual persistent store identity.
635        pub store: u128,
636        /// Actual reopened graph generation.
637        pub generation: u64,
638        /// Actual reopened complete-envelope sequence.
639        pub sequence: u64,
640        /// First created node identity.
641        pub first_node: u128,
642        /// Second created node identity.
643        pub second_node: u128,
644        /// Actual relationship row observed in both directions.
645        pub relationship: ObservedRelationship,
646    }
647
648    /// Receipts emitted only after each directed recovery body completes.
649    #[derive(Clone, Debug, Eq, PartialEq)]
650    pub struct RecoveryProbeReport {
651        /// Completed path receipts with actual fired/clean counts.
652        pub receipts: Vec<PathReceipt>,
653        /// Actual reopened mixed state.
654        pub state: RecoveryState,
655    }
656
657    /// Exercises seal rotation with a graph record after the previous fold.
658    pub fn run_seal_rotation_probe() -> Vec<PathReceipt> {
659        crate::lifecycle::native_graph::tests::seal::run_rotation_probe()
660    }
661
662    /// Exercises late seal cancellation and failures of its durable cleanup.
663    pub fn run_seal_cancellation_probe() -> Vec<PathReceipt> {
664        crate::lifecycle::native_graph::tests::seal::run_cancellation_probe()
665    }
666
667    /// Exercises interrupted enable catalog creation and writable retry.
668    pub fn run_enable_retry_probe() -> Vec<PathReceipt> {
669        crate::lifecycle::native_graph::tests::recovery::run_enable_retry_probe()
670    }
671
672    /// Runs the nine directed production recovery paths used by ZE-40 acceptance.
673    pub fn run_actual_probe(seed: u64) -> RecoveryProbeReport {
674        crate::lifecycle::native_graph::tests::run_recovery_probe(seed)
675    }
676}
677
678/// Nonshipping public-facade observations for seeded ZE-41 qualification.
679#[cfg(all(feature = "graph-cypher", any(test, feature = "test-seams")))]
680pub mod graph_commit_recovery_test_support {
681    pub use crate::property_graph::commit_recovery_test_support::*;
682}
683
684/// Query diagnostics and health reporting.
685pub mod diag;
686/// Epoch identity and migration.
687pub mod epoch;
688/// Persisted-format framing, versions, and golden-fixture support.
689pub mod format;
690/// Full-text indexing and retrieval.
691pub mod fts;
692/// Hybrid result fusion.
693pub mod fusion;
694/// Per-segment vector graphs.
695pub mod graph;
696/// Ingest and mutation coordination.
697pub mod ingest;
698/// Runtime-dispatched compute kernels.
699pub mod kernels;
700/// Store lifecycle and memory accounting.
701pub mod lifecycle;
702/// Persistent manifest coordination.
703pub mod manifest;
704/// Columnar metadata and filters.
705pub mod meta;
706/// Query planning and selectivity decisions.
707pub mod planner;
708/// Native property-graph identity and values, independent of vector indexes.
709#[cfg(feature = "graph-cypher")]
710pub mod property_graph;
711/// Training-free vector quantization.
712pub mod quant;
713/// Exact vector scanning.
714pub mod scan;
715/// Immutable segment representation.
716pub mod segment;
717/// Operating-system integration wrappers.
718pub mod sys;
719/// Adaptive storage tiers.
720pub mod tier;
721/// Read-only end-to-end store verification.
722pub mod verify;
723/// Virtual filesystem and platform I/O.
724pub mod vfs;
725/// Write-ahead logging and durability.
726pub mod wal;
727
728#[cfg(test)]
729mod test_support;
730
731/// The current crate version.
732pub const VERSION: &str = env!("CARGO_PKG_VERSION");
733
734#[cfg(test)]
735#[allow(
736    clippy::expect_used,
737    clippy::indexing_slicing,
738    clippy::panic,
739    clippy::unwrap_used
740)]
741mod tests {
742    use rand::RngCore;
743
744    #[test]
745    #[cfg_attr(miri, ignore = "environment-dependent RNG is outside the Miri subset")]
746    fn seeded_rng_is_deterministic_per_name_and_env() {
747        let mut first =
748            crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
749        let mut second =
750            crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
751        let mut other_name = crate::test_support::seeded_rng("tests::another_test");
752
753        let first_draw = first.next_u64();
754        let second_draw = second.next_u64();
755        let other_name_draw = other_name.next_u64();
756
757        let environment_seed = std::env::var("ZE_TEST_SEED").unwrap_or_else(|_| String::from("0"));
758        let other_environment_seed = format!("{environment_seed}-different");
759        let mut other_env = crate::test_support::seeded_rng_from(
760            "tests::seeded_rng_is_deterministic_per_name_and_env",
761            &other_environment_seed,
762        );
763        let other_env_draw = other_env.next_u64();
764
765        assert_eq!(first_draw, second_draw);
766        assert_ne!(first_draw, other_name_draw);
767        assert_ne!(first_draw, other_env_draw);
768    }
769
770    #[test]
771    fn version_constant_is_current() {
772        assert_eq!(crate::VERSION, "0.7.0");
773    }
774}