ruvector/crates/ruvector-rulake/tests
ruvnet 0ceba2a032 feat(rabitq,rulake): persist end-to-end — save_cache_to_dir + warm_from_dir
Wires the previously-shipped rabitq::persist module into ruLake's
lake.rs as first-class cache-save/restore APIs. The architectural
blocker I've deferred across 3 waves is now closed.

=== Agent A: rabitq::RabitqPlusIndex::export_items() ===
crates/ruvector-rabitq/src/index.rs +1 method, +1 test.

Exposes `export_items() -> Vec<(usize, Vec<f32>)>` — each row as
(pos, original_vec) extracted from originals_flat with one clone per
row. Feeds directly into persist::save_index or
from_vectors_parallel_with_rotation. No new deps, no public API
breakage.

Regression test (`export_items_roundtrip_via_from_vectors_parallel`)
builds via serial add(), exports, rebuilds via the parallel path,
asserts byte-identical search results on 5 queries. Tests: 36 → 37.

=== Agent B: RuLake save_cache_to_dir + warm_from_dir ===
crates/ruvector-rulake/src/{cache.rs, lake.rs, tests/federation_smoke.rs}.

New API:
  pub fn save_cache_to_dir(&self, key, dir) -> Result<PathBuf>
    — writes dir/index.rbpx (atomic temp+rename+fsync) alongside
      the table.rulake.json bundle sidecar. Uses export_items +
      persist::save_index.
  pub fn warm_from_dir(&self, key, dir) -> Result<usize>
    — reads bundle, witness-verifies, loads index.rbpx via
      persist::load_index, cross-checks dim+rerank_factor, installs
      into cache via the new install_prebuilt path. Returns n vectors.
      Does NOT require the backend to be registered — warm restart
      without backend RTT is the point.

New on CacheStats: warm_installs counter (separate from primes so
warm-restart cost isn't confused with cold-prime cost).

New on VectorCache: install_prebuilt + install_prebuilt_interned —
insert a pre-built Arc<RabitqPlusIndex> at a known witness without
any prime-timer bookkeeping. Respects the LRU cap. Shared-entry
path reuses an existing witness entry if another pointer already
holds it (witness-addressed cache sharing remains the headline).

New test: `warm_from_dir_skips_backend_and_returns_bit_exact_results`
Prime a 50-vec D=8 collection, save, spin up a FRESH RuLake with
NO backend registered + Consistency::Frozen, warm_from_dir, run the
same query, assert byte-identical ids + f32 score bits,
warm_installs=1, primes=0. Closes the "restart without re-prime"
gap end-to-end.

Documented limitation: pos_to_id reconstructed as (0..n) identity
because RabitqPlusIndex doesn't expose outer ids() accessor, and
the rabitq agent's scope prohibited adding it. Every current prime
path uses positional ids so this is byte-equivalent to the real
ids; external non-dense u64 ids would collapse (a known M2+ issue
filed inline).

Tests: 37 rabitq + 21 rulake lib + 22 rulake federation = 80 total.
Clippy -D warnings clean across both crates.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-23 23:34:38 -04:00
..
federation_smoke.rs feat(rabitq,rulake): persist end-to-end — save_cache_to_dir + warm_from_dir 2026-04-23 23:34:38 -04:00