feat(turbovec): multi-bit TurboQuant FastScan ANN index (ADR-194 M1)#521
feat(turbovec): multi-bit TurboQuant FastScan ANN index (ADR-194 M1)#521shaal wants to merge 11 commits into
Conversation
…N index Research and scope a new crate adapting TurboQuant techniques from RyanCodrai/turbovec: 2/4-bit Lloyd-Max scalar quantization, TQ+ per-coordinate calibration, length-renormalized unbiased scoring, and a nibble-LUT FastScan SIMD kernel (AVX-512BW/AVX2/NEON). Reuses ruvector-rabitq's Hadamard rotation + AnnIndex/VectorKernel traits and borrows ruvllm's MSE quantizer math, closing the missing 2-4-bit FastScan ANN regime. https://claude.ai/code/session_012AzArCzBwxrJp8mUngUcH5
New crate crates/ruvector-turbovec implementing the scalar-reference milestone of ADR-194 — the 2/4-bit FastScan-style ANN regime ruvector lacked (rabitq is 1-bit; ruvllm's TurboQuant is a KV-cache codec, not a search index). Pipeline: normalize -> randomized Hadamard rotation (reused from ruvector-rabitq) -> TQ+ per-coordinate calibration -> Lloyd-Max 2/4-bit scalar quantization -> per-vector length-renormalized unbiased scoring. Implements the shared ruvector_rabitq::AnnIndex trait. Adds IdMapIndex with O(1) delete and allowlist-filtered search. Proof (cargo run --release -p ruvector-turbovec), n=5000 uniform-random vectors, dim=256, k=10, no f32 rerank, vs exact brute-force L2: 1-bit: recall@10 0.308, 25.6x compression, bias +0.0005 2-bit: recall@10 0.561, 14.2x compression, bias +0.0001 4-bit: recall@10 0.879, 7.5x compression, bias -0.0000 Recall rises monotonically with bit-width; mean cosine bias ~0 confirms the unbiased estimator. Determinism + IdMap delete/filter verified. 12 unit tests + 1 doc-test pass; build green; clippy clean. M2-M4 (FastScan nibble-LUT SIMD kernel, AVX-512, dispatcher) are future milestones; the scalar scorer here is their determinism oracle. https://claude.ai/code/session_012AzArCzBwxrJp8mUngUcH5
… errors - index: TurboVecIndex::add/search now return RabitqError::DimensionMismatch in release builds instead of silently accepting/masking wrong-length vectors (was debug_assert + unwrap_or_default). - index: finalize() excludes zero vectors from calibration fit so they don't bias shift/scale toward zero. - idmap: add_with_id validates dim up front and reports the real length (was hardcoded got: 0); add_with_ids rejects vectors/ids length mismatch with new TurboVecError::BatchLenMismatch instead of zip-truncating. - quantize: pack/unpack document preconditions and debug_assert code-range and slice-length (proportionate to internal helpers; no Result churn). - calibrate: fit debug_asserts every row has length dim. - ADR-194 frontmatter status proposed -> accepted to match body. Adds 4 tests (wrong-dim reject on add/search, zero-vector calibration exclusion via self-retrieval, batch-len mismatch, idmap wrong-dim). 16 unit tests + 1 doc-test pass; clippy clean; demo unchanged. https://claude.ai/code/session_012AzArCzBwxrJp8mUngUcH5
- Cargo.toml: remove unused rand_distr dependency and the redundant rand dev-dependency (rand is a normal dep for the demo bin + tests). - Cargo.lock: drop rand_distr from ruvector-turbovec. - ADR-194: attribute the FAISS-competitive figures to the upstream RyanCodrai/turbovec project rather than presenting them as this crate's measured results; point readers to the reproducible uniform-random Validation table instead. No code changes; 16 unit + 1 doc-test still pass, clippy clean.
Add an explicit 'Divergences from the TurboQuant paper (arXiv:2504.19874)' section mapping where M1 departs from the paper, so the gaps are reviewable and the follow-ups are paper-grounded: - D1: M1 uses a heuristic per-vector c_x scale, not the paper's provably- unbiased two-stage MSE + 1-bit-QJL-residual estimator. Soften the T4 and Validation wording accordingly (empirically near-unbiased, not proven). - D2: M1 quantizes against the N(0,1) limit + empirical TQ+ calibration, not the paper's exact d-aware Beta-optimal codebooks. - D3: M1 ships 1/2/4-bit; paper highlights ~2.5/3.5 bpc sweet spots — add 3-bit. - D4: assert measured distortion under the paper's closed-form bounds as a stronger test oracle than recall > 0.5. - D5: estimator variance deferred. Add milestones M5 (paper-grade QJL-residual estimator) and M6 (Beta-optimal codebooks); note what M1 already matches (norm-based L2, online ingest, full-precision query). No code change.
…ll cliff
Adds BitWidth::Three (8-level Max-1960 optimal N(0,1) reconstruction
levels). pack/unpack, calibration, scoring, and IdMap are width-generic,
so only the centroid table + the enum arms change.
Measured (cargo run --release -p ruvector-turbovec, n=5000 uniform-random,
dim=256, k=10, no rerank, vs exact L2):
3-bit: recall@10 0.767, 112 B/vec, 9.8x compression, bias -0.0000
landing squarely between 2-bit (0.561) and 4-bit (0.879) — a useful
memory/recall midpoint (~22% smaller than 4-bit for ~0.11 recall).
Also refresh ADR-194: add the 3-bit Validation row, mark D3 done, widen
T2 to {2,3,4}, correct the test count to 16, and scope the provenance
note so the measured recall/compression/bias figures are called measured
while the FAISS-competitive claims stay attributed targets.
16 unit + 1 doc-test pass; clippy clean; new code is rustfmt-clean.
Pure rustfmt normalization (rustfmt 1.8.0-stable) of lines that predated this branch; no semantic change. Isolated from the feature commits so the crate passes 'cargo fmt --check' cleanly for upstream CI.
Add quantizer_mse_within_paper_bound: draw 400k N(0,1) samples (Box–Muller, no new deps), quantize via the real quantize_coord path, and assert the per-coordinate MSE for every width stays under TurboQuant's distortion bound D_mse ≤ (√3·π/2)·4^(−b) (arXiv:2504.19874) AND within 5% of the Max-1960 Lloyd–Max optimum. A corrupted centroid level trips this far more precisely than the existing recall>0.5 threshold. Marks D4 done in ADR-194; updates test count to 17. The full-pipeline inner-product bound D_prod remains future work (tracked with D5).
Canonical ADR for the 2-4-bit scalar-quantized FastScan search index proposed in #520 / PR #521. Numbered 254 because the PR drafted it as ADR-194, which collides with the merged ADR-194 (ONNX embedder). Captures the gap, the T1-T6 design, reuse boundary, milestones M1-M5, measured M1 validation, and honest divergences from the TurboQuant paper. Co-Authored-By: claude-flow <ruv@ruv.net>
|
ADR number collision — needs renumber. This PR adds I've reviewed #520 and authored the canonical record at the next free number — ADR-254 ( To land this PR cleanly, please |
ADR-194 is already taken on main (ruvector ONNX embedder API & throughput). Renumber this PR's turbovec ADR to the next free number (254), matching the canonical record on main. Keeps the fuller PR version (D1–D5 divergences table, D3/D4 measured-milestone markers) and adds a numbering note. Updates the 13 in-crate ADR-194 references and two stray ADR-193 'future work' pointers so they no longer resolve to the unrelated ONNX ADR. Refs ruvnet#520, ruvnet#521
…vec-optimization-FhaDh # Conflicts: # docs/adr/ADR-254-ruvector-turbovec-fastscan-index.md
|
Addressed the ADR-194 collision flagged above — took option 1 (renumber, keep this PR's fuller draft). Done (commits
One note for reconciliation: this branch's |
…35) (#3) * fix(ruvector-wasm): correct adapter for WASM build's flat-index, distance-score, and metadata gaps (ruvnet#568) The published @ruvector/wasm build behaves differently from its generated .d.ts in three ways that bite consumers: 1. HNSW is not active — the wasm32 target compiles without the `hnsw` feature and falls back to a flat (brute-force) index, so search is O(n). The O(log n) win is latent until the WASM HNSW lands. 2. `result.score` is a cosine distance (lower is better), not the "higher is better" similarity the .d.ts advertises (ordering is correct: a, b before c). 3. Metadata does not round-trip — search/get return {}. Add RuvectorWasmAdapter (@ruvector/wasm/adapter) which wraps VectorDB with: - a metadata sidecar so inserted metadata round-trips - similarity = 1 - distance (generalised per metric) with `.score` aliased to similarity, plus the raw `distance` preserved - indexType/usesHnsw + WASM_HNSW_AVAILABLE so callers don't assume HNSW - client-side metadata filtering with over-fetch Includes TS declarations with corrected doc comments, a node:test suite covering all three findings, README guidance, and package exports. Co-authored-by: Claude <noreply@anthropic.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 08c0d74 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore(ruvector-wasm): publish @ruvector/wasm 0.1.31 with corrected adapter Ships the RuvectorWasmAdapter (ruvnet#568) and restores a functional package — 0.1.30 published with only package.json (empty pkg/). 0.1.31 includes the built web pkg/ plus the adapter that corrects similarity score, metadata round-trip, and flat-index reporting. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 054d815 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(tiny-dancer): lock-step platform versioning + drop dead release profile Root cause of version drift: build-tiny-dancer.yml hard-coded VERSION="0.1.15", so every publish shipped stale platform binaries while the main package advanced (npm 0.1.18 was loading 0.1.15-era .node files). - workflow: derive VERSION from package.json; rewrite main optionalDependencies to pin that exact version before publishing, so binaries and JS can never skew. - package.json: bump to 0.1.19, pin all 5 optionalDependencies to 0.1.19, remove dead `publish:platforms` script (scripts/publish-platforms.js absent). - crate Cargo.toml: remove [profile.release] (Cargo ignores non-root profiles; release opt is already opt-level=3/lto=fat/codegen-units=1/strip at root). Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 57187b0 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(tiny-dancer): full platform matrix — add musl (Alpine) + Windows ARM64 Extends the build/publish workflow from 5 to 8 environments: - linux-x64-musl, linux-arm64-musl (Alpine/Docker) via napi --use-napi-cross - win32-arm64-msvc (Windows on ARM) via rustup target cross-compile index.js now detects musl vs glibc at load time (process.report glibc header) and routes to the correct binary; adds win32-arm64. package.json bumped to 0.1.20 with all 8 optionalDependencies lock-stepped. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 1ef4eb8 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(tiny-dancer): use --zig + setup-zig for musl cross-builds (napi 2.18 has no --use-napi-cross) Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 4c448f0 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(tiny-dancer): real FastGRNN training pipeline (ADR-252) Closes the three gaps that made tiny-dancer inference-only: 1. Real gradients: FastGRNN::forward_cached + backward implement single-step analytic backprop (h0=0); gradient-checked vs central finite differences. 2. Real Adam step: train_batch accumulates mean batch gradients; apply_gradients does L2 + global-norm clip + bias-corrected Adam update on the existing optimizer state. Model now actually learns (test: loss down, acc>0.9). 3. safetensors persistence: model.rs save/load serialize every tensor (f32 LE) with config in __metadata__; round-trip is bit-exact. 4. DRACO adapter: TrainingDataset::from_draco consumes the {embedding, scores} + prices shape (same as @metaharness/router) so one dataset trains both. Runnable example train_from_draco demonstrates DRACO -> train -> save -> load -> route end to end. 31 core tests green (gradient check, convergence, round-trip, adapter). Co-Authored-By: claude-flow <ruv@ruv.net> * feat(tiny-dancer-node): napi trainRouter export (ADR-252 B) Exposes the FastGRNN training pipeline to JS: trainRouter(rows, prices, opts) consumes DRACO {embedding, scores} rows, trains, and writes a .safetensors the existing `new Router({ modelPath })` loads. Type-checks; reaches JS on the next tiny-dancer platform rebuild/republish. Co-Authored-By: claude-flow <ruv@ruv.net> * release(tiny-dancer): 0.1.21 — expose trainRouter to JS (types + bump) Adds trainRouter/DracoRowJs/TrainRouterOptions/TrainRouterResult to the hand-written index.d.ts and bumps to 0.1.21 (8 optionalDependencies lock-stepped) so the native train_router export ships typed across all environments. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit e709718 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 1aa132b Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit c520576 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(ruvector-cli): integrate tiny-dancer router (train/route/info) npx ruvector tiny-dancer train <draco.json> --out model.safetensors trains a FastGRNN cost-optimal router from a DRACO {embedding,scores} dataset; `route` loads + routes; `info` reports availability. @ruvector/tiny-dancer added as an optionalDependency (native binary, lazy-required with install hint). Bump 0.2.31. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(tiny-dancer): add score() raw-forward inference + 0.1.22 score(modelPath, embedding) loads a trainRouter .safetensors and runs the FastGRNN directly on the raw embedding (the inference path that matches how trainRouter trains — Router.route's 5-feature engineering is a different model contract). Validated easy=1.0/hard=0.0 on a freshly built binary. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(ruvector-cli): tiny-dancer score command (raw forward) + dep ^0.1.22 Replaces the route subcommand (which used Router.route's 5-feature engineering — a different model contract) with 'score', backed by tiny-dancer's score() raw forward pass that matches trainRouter models. Also fixes the earlier Float32Array issue by removing the Router.route path entirely. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 39fb398 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 5173ce7 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 3c1f701 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (ruvnet#570) * docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities Compares HelixDB (LMDB/heed, compiled type-safe HelixQL, graph-vector thesis, graph-vector-bench) against RuVector's redb/Cypher/hybrid stack and proposes 7 prioritized, opt-in improvements: optional schema layer with load-time validation, first-class typed graph-vector binding and a unified search-then-traverse operator, in-query embed(), unified ANN+BM25+graph RRF hybrid, a reproducible benchmark harness, schema-driven typed SDK codegen, and an object-storage tier research spike. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): native schema layer + typed search-then-traverse (ADR-252 P1/P2/P4) Implements the HelixDB-inspired improvements natively in ruvector-graph: - schema.rs: opt-in GraphSchema (N::/E::/V:: equivalents) with load-time validation (self-consistency, node required/typed props + strict mode, edge from/to label constraints, vector dimension checks), higher-is-better distance metrics (cosine/dot/euclidean), and reciprocal_rank_fusion (P4). - typed_graph.rs: TypedGraph wrapper validating mutations pre-storage, plus a fused typed search_then_traverse operator (HelixQL SearchV<T>(q,k)::In/Out<E>) with optimized bounded-heap top-k selection (O(n log k)). Pure-Rust, no new deps, WASM-safe. 13 new tests, 148/148 lib tests green, clippy clean. Schemaless mode remains the default (opt-in coexistence). https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * perf(ruvector-graph): optimize search_then_traverse + add criterion bench (ADR-252) Hot-path optimizations for the typed search-then-traverse operator: - GraphDB::with_node / node_ids_by_label: zero-copy borrow scoring, eliminating per-candidate Node + embedding clones (get_nodes_by_label cloned everything). - Fused single-pass cosine (q.c and c.c in one read of the candidate) + hoisted query norm out of the per-candidate loop. - Bounded top-k min-heap (O(n log k)); clone id only for heap winners. - Rayon parallel scan over DashMap for >=4096 candidates (per-thread heaps, bounded merge); serial path below threshold. Adds benches/typed_graph_bench.rs (criterion). Measured vs first cut (128-dim, k=10): 10k 7.2ms->3.08ms (2.34x), 50k 74.3ms->28.5ms (2.61x), 1k 539us->432us. New parallel-vs-reference correctness test. 149/149 lib tests green, clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): HNSW push-down for search_then_traverse (ADR-252 P2) Adds an opt-in ANN path to the typed search-then-traverse operator, removing the O(n) full-label scan for indexed vector types: - TypedGraph::build_vector_index(vector_type) builds a per-vector-type HybridIndex (HNSW under hnsw_rs, exact FlatIndex otherwise), holding only the bound label's nodes so searches stay label-scoped. Kept current incrementally via create_node -> index_node. - search_then_traverse routes through the index when present: ~O(log n) approximate search, over-fetch (max(4k, k+32)), then exact rescore with the schema metric so ANN results carry identical higher-is-better score semantics to the brute-force path. Brute force remains the default. - Parallel brute-force path refactored to capture &GraphDB (not &self) so it stays Send+Sync independent of the index's thread-safety bounds. Bench (50k nodes, 128-dim, k=10): brute-force parallel scan 27.6ms -> HNSW push-down 1.05ms (~26x; ~70x vs first cut). 151/151 lib tests green (3 new HNSW tests), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): inline embed() + tri-modal BM25/ANN/graph hybrid (ADR-252 P3/P4) P3 - inline embedding (HelixQL Embed()): - embed.rs: Embedder trait + dependency-free deterministic HashEmbedder (feature-hashing, explicit opt-in, never a silent fallback per ADR-194). - TypedGraph::with_embedder / embed / create_node_from_text (embed-at-insert, dimension-validated) / search_text (embed-at-query). P4 - tri-modal hybrid query: - bm25.rs: self-contained Okapi-BM25 inverted index. - TypedGraph::build_text_index + hybrid_search_text fusing ANN vector + BM25 keyword + graph traversal via reciprocal rank fusion in one typed call. - Refactored search_then_traverse into shared rank_seeds/expand helpers. Bench: hash_embed_256 717ns; tri_modal_hybrid over 10k docs (embed+HNSW+BM25+ RRF+traverse) 1.63ms end-to-end. 164/164 lib tests green (+13), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): schema-driven typed SDK codegen (ADR-252 P6) codegen.rs generates typed client stubs from a GraphSchema: - generate_typescript: interfaces with typed/optional properties (@indexed hints), edge from->to constraints, and a VectorTypes manifest + VectorTypeName. - generate_python: TypedDict classes + VECTOR_TYPES manifest. - generate_rust: serde-ready structs. Deterministic (schema elements sorted) for check-in/diff. Adds *_schemas_sorted accessors to GraphSchema. Closes HelixDB's schema->typed-SDK DX advantage. 168/168 lib tests green (+4), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * docs(adr): renumber ADR-252 -> ADR-253 (252 taken by FastGRNN training pipeline) ADR-252 was already merged to main as the tiny-dancer FastGRNN training pipeline. Renumber this HelixDB comparison to ADR-253 to resolve the collision. Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 1e1740a Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(mcp-brain-server): use native-tls-vendored so it cross-compiles to aarch64 (ruvnet#573) mcp-brain-server pins reqwest with the `native-tls` feature (for TLS close_notify compatibility). On aarch64 cross-builds, native-tls → openssl-sys fails because the target's OpenSSL dev headers aren't present, so the binary can't be built for Raspberry Pi 5. Switch to `native-tls-vendored`, which builds OpenSSL from source for the target — identical TLS behavior (still native-tls, close_notify compat preserved), just statically linked, no host/target OpenSSL headers required. Context: the Cognitum v0 appliance image (cognitum-one/v0-appliance) builds this binary as `ruview-mcp-brain-mini` (the :9876 vector-memory store) from this repo as a submodule. This is the only blocker preventing it from shipping on the Pi 5 (Hailo-8/10H) appliance. * chore: Update NAPI-RS binaries for all platforms Built from commit e346476 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(ruvector-core): data-loss in update_q_value (ruvnet#562) + silent quantization no-op (ruvnet#563) ruvnet#562 (Critical): PolicyMemoryStore::update_q_value deleted the policy and returned Ok(()) without re-inserting — silent data loss for any RL loop. Now it fetches the entry, updates q_value in metadata, and re-inserts (preserving the embedding); returns VectorNotFound on a missing id instead of false success. Adds a regression test. ruvnet#563 (High): DbOptions.quantization was accepted/persisted but never threaded into the index/storage, while the default was Some(Scalar) and docs promised 4x/32x compression. Default flipped to None; VectorDB::new now warns when a non-None quantization is set; enum/field docs state compression is a target "not yet applied", not a guarantee. 228 ruvector-core lib tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 135a030 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-254 ruvector-turbovec multi-bit FastScan ANN index (ruvnet#520) Canonical ADR for the 2-4-bit scalar-quantized FastScan search index proposed in ruvnet#520 / PR ruvnet#521. Numbered 254 because the PR drafted it as ADR-194, which collides with the merged ADR-194 (ONNX embedder). Captures the gap, the T1-T6 design, reuse boundary, milestones M1-M5, measured M1 validation, and honest divergences from the TurboQuant paper. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit a7028ef Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-255 ruvector <-> OIA Model integration (alignment profile) Grounded in a deep-research brief over agenticsorg/OIA-Model v0.1: maps OIA's 10 layers (L0-L9) + 6 spans to ruvector components, decides a non-binding alignment profile (ruvector as an L3 + L5-L8 provider), designates the RVF cognitive container as the L8 artifact and the witness chain as the SPAN-AUD/PRV primitive, and explicitly scopes out L0/L1/L9/L4-pretraining + the GCP-portability gap. Stays doc/tag-level — no OIA dependency, no API rename — because OIA is pre-1.0 with no machine-readable conformance. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 183ed4a Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(security): SECURITY.md disclosure policy (ruvnet#320) + CORS allowlist (ruvnet#560) (ruvnet#577) - Add SECURITY.md: private disclosure via GitHub PVR or ruv@ruv.net, scope, and response SLAs. Closes the responsible-disclosure gap raised in ruvnet#320 (gives reporters a channel without enabling beg-bounty noise). - mcp-brain-server CORS: add https://app.conceptmapping.org and https://conceptmapping.org to the default allowlist so pi.ruv.io/v1/* returns Access-Control-Allow-Origin for those browser origins (ruvnet#560). Kept an explicit per-origin allowlist (not `*`) since callers authenticate with Bearer tokens. cargo check -p mcp-brain-server: clean. Refs ruvnet#320 ruvnet#560 Co-authored-by: ruv <ruvnet@users.noreply.github.com> * ADR-256: harness router surface (borrow metaharness concepts) (ruvnet#575) * feat(ruvector): ADR-256 harness router surface + tracking (ruvnet#574) Borrow metaharness concepts using primitives ruvector already ships. - Add `ruvector harness status [--json]` — unified read-only view of the routing surface (Tiny Dancer cost router + semantic router + hooks routing + MCP + witness + memory), degrading gracefully when optional deps are absent. Implements ADR-256 rollout step 0. - Add ADR-256 (borrow-concepts decision, concept→primitive mapping). - Add CLI tests (Section 24): harness --help, status --json structure, bare-command behavior. Full suite: 72 passed, 0 failed. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * feat(ruvector): ADR-256 default-deny MCP tool-access policy (ruvnet#574) Borrow metaharness's default-deny allowlist concept with our own machinery. - New pure, testable bin/mcp-policy.js: RUVECTOR_MCP_ALLOW / RUVECTOR_MCP_DENY / RUVECTOR_MCP_PROFILE=readonly. Precedence DENY > ALLOW/PROFILE > allow-all. No policy set = backward-compatible allow-all (policy.configured=false). - Wire into mcp-server.js: ListTools now returns only permitted tools; CallTool gates denied tools with an isError response before dispatch. - harness status --json now reports mcp.policy + accessControl posture. - Tests: test/mcp-policy.js (8 unit tests) wired into npm test; verified end-to-end over MCP stdio (readonly profile exposes 10 safe tools, filters hooks_force_learn). CLI suite still 72/0. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * test(ruvector): ADR-256 startup-budget guard + harness/MCP-policy docs (ruvnet#574) - New test/startup-budget.js wired into npm test: absolute ceiling on `--help` cold start + relative delta guard ensuring `harness status` adds < 120ms over baseline (catches a heavy module leaking into the startup path). Measured here: --help 127ms, harness +3ms. Env-overridable. - README: document the default-deny MCP policy env vars (RUVECTOR_MCP_ALLOW/DENY/PROFILE) and the `harness` router command. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * feat(ruvector): ADR-256 memory namespace + full verification, ADR status (ruvnet#574) - harness surface reports a stable memory namespace (RUVECTOR_MEMORY_NAMESPACE, default `ruvector`); CLI tests assert the default + override and the MCP accessControl/policy fields. - README documents the memory namespace. - ADR-256: add "Implementation status (as shipped)" — items 0/1/3/4 done, benchmarked + full npm test green; item 2 as a documented convention; item 5 deferred. No @metaharness/* runtime dep. Full suite: cli 73/0, mcp-policy 8/0, startup-budget 2/0, db-workflow/integration/sigterm green. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore(ruvector): bump to 0.2.32 (ADR-256 release prep) (ruvnet#578) * chore(ruvector): bump to 0.2.32 (harness router + default-deny MCP policy) Release prep for the ADR-256 work merged in ruvnet#575: - `ruvector harness status [--json]` unified routing surface - default-deny MCP tool policy (RUVECTOR_MCP_ALLOW/DENY/PROFILE) - stable memory namespace (RUVECTOR_MEMORY_NAMESPACE) - startup-budget CI guard Additive / non-breaking → patch bump. Tests green (73/0, 8/0, 2/0). Does NOT publish — `npm publish` left to a maintainer with npm auth. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(ruvector): cross-platform build script (node fs.cpSync) The build copied ONNX assets with `mkdir -p`/`cp -r`, which fail on Windows cmd.exe and broke `npm publish` (prepack/prepublishOnly) off-Linux. Replace with node fs.cpSync(recursive) — works everywhere. Verified: build + verify-dist green on Windows (14 dist paths present). Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 716dbed Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit a2c5ce4 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(wasm): unblock wasm bridge builds (getrandom/uuid js, wasm-opt=false) Apply RuVector-canonical wasm fixes (verified vs passing -wasm crates): - wasm-opt = false for bulk-memory validator failures (delta, fpga-transformer, graph-condense, mincut-gated-transformer, solver, sparse-inference) - getrandom { features=[js] } + uuid { features=[js] } for wasm32 (router, tiny-dancer) Result: 24 -> 29 of 35 -wasm crates build. Remaining 6 have distinct deeper causes (sqlite-C not wasm-able, arithmetic_overflow lint, non-member crates). Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: rUv <ruv@ruv.net> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: ruv <ruvnet@users.noreply.github.com> Co-authored-by: Dragan Spiridonov <spiridonovdragan@gmail.com>
…ing (33/35) (#4) * fix(ruvector-wasm): correct adapter for WASM build's flat-index, distance-score, and metadata gaps (ruvnet#568) The published @ruvector/wasm build behaves differently from its generated .d.ts in three ways that bite consumers: 1. HNSW is not active — the wasm32 target compiles without the `hnsw` feature and falls back to a flat (brute-force) index, so search is O(n). The O(log n) win is latent until the WASM HNSW lands. 2. `result.score` is a cosine distance (lower is better), not the "higher is better" similarity the .d.ts advertises (ordering is correct: a, b before c). 3. Metadata does not round-trip — search/get return {}. Add RuvectorWasmAdapter (@ruvector/wasm/adapter) which wraps VectorDB with: - a metadata sidecar so inserted metadata round-trips - similarity = 1 - distance (generalised per metric) with `.score` aliased to similarity, plus the raw `distance` preserved - indexType/usesHnsw + WASM_HNSW_AVAILABLE so callers don't assume HNSW - client-side metadata filtering with over-fetch Includes TS declarations with corrected doc comments, a node:test suite covering all three findings, README guidance, and package exports. Co-authored-by: Claude <noreply@anthropic.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 08c0d74 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore(ruvector-wasm): publish @ruvector/wasm 0.1.31 with corrected adapter Ships the RuvectorWasmAdapter (ruvnet#568) and restores a functional package — 0.1.30 published with only package.json (empty pkg/). 0.1.31 includes the built web pkg/ plus the adapter that corrects similarity score, metadata round-trip, and flat-index reporting. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 054d815 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(tiny-dancer): lock-step platform versioning + drop dead release profile Root cause of version drift: build-tiny-dancer.yml hard-coded VERSION="0.1.15", so every publish shipped stale platform binaries while the main package advanced (npm 0.1.18 was loading 0.1.15-era .node files). - workflow: derive VERSION from package.json; rewrite main optionalDependencies to pin that exact version before publishing, so binaries and JS can never skew. - package.json: bump to 0.1.19, pin all 5 optionalDependencies to 0.1.19, remove dead `publish:platforms` script (scripts/publish-platforms.js absent). - crate Cargo.toml: remove [profile.release] (Cargo ignores non-root profiles; release opt is already opt-level=3/lto=fat/codegen-units=1/strip at root). Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 57187b0 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(tiny-dancer): full platform matrix — add musl (Alpine) + Windows ARM64 Extends the build/publish workflow from 5 to 8 environments: - linux-x64-musl, linux-arm64-musl (Alpine/Docker) via napi --use-napi-cross - win32-arm64-msvc (Windows on ARM) via rustup target cross-compile index.js now detects musl vs glibc at load time (process.report glibc header) and routes to the correct binary; adds win32-arm64. package.json bumped to 0.1.20 with all 8 optionalDependencies lock-stepped. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 1ef4eb8 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(tiny-dancer): use --zig + setup-zig for musl cross-builds (napi 2.18 has no --use-napi-cross) Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 4c448f0 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(tiny-dancer): real FastGRNN training pipeline (ADR-252) Closes the three gaps that made tiny-dancer inference-only: 1. Real gradients: FastGRNN::forward_cached + backward implement single-step analytic backprop (h0=0); gradient-checked vs central finite differences. 2. Real Adam step: train_batch accumulates mean batch gradients; apply_gradients does L2 + global-norm clip + bias-corrected Adam update on the existing optimizer state. Model now actually learns (test: loss down, acc>0.9). 3. safetensors persistence: model.rs save/load serialize every tensor (f32 LE) with config in __metadata__; round-trip is bit-exact. 4. DRACO adapter: TrainingDataset::from_draco consumes the {embedding, scores} + prices shape (same as @metaharness/router) so one dataset trains both. Runnable example train_from_draco demonstrates DRACO -> train -> save -> load -> route end to end. 31 core tests green (gradient check, convergence, round-trip, adapter). Co-Authored-By: claude-flow <ruv@ruv.net> * feat(tiny-dancer-node): napi trainRouter export (ADR-252 B) Exposes the FastGRNN training pipeline to JS: trainRouter(rows, prices, opts) consumes DRACO {embedding, scores} rows, trains, and writes a .safetensors the existing `new Router({ modelPath })` loads. Type-checks; reaches JS on the next tiny-dancer platform rebuild/republish. Co-Authored-By: claude-flow <ruv@ruv.net> * release(tiny-dancer): 0.1.21 — expose trainRouter to JS (types + bump) Adds trainRouter/DracoRowJs/TrainRouterOptions/TrainRouterResult to the hand-written index.d.ts and bumps to 0.1.21 (8 optionalDependencies lock-stepped) so the native train_router export ships typed across all environments. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit e709718 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 1aa132b Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit c520576 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * feat(ruvector-cli): integrate tiny-dancer router (train/route/info) npx ruvector tiny-dancer train <draco.json> --out model.safetensors trains a FastGRNN cost-optimal router from a DRACO {embedding,scores} dataset; `route` loads + routes; `info` reports availability. @ruvector/tiny-dancer added as an optionalDependency (native binary, lazy-required with install hint). Bump 0.2.31. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(tiny-dancer): add score() raw-forward inference + 0.1.22 score(modelPath, embedding) loads a trainRouter .safetensors and runs the FastGRNN directly on the raw embedding (the inference path that matches how trainRouter trains — Router.route's 5-feature engineering is a different model contract). Validated easy=1.0/hard=0.0 on a freshly built binary. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(ruvector-cli): tiny-dancer score command (raw forward) + dep ^0.1.22 Replaces the route subcommand (which used Router.route's 5-feature engineering — a different model contract) with 'score', backed by tiny-dancer's score() raw forward pass that matches trainRouter models. Also fixes the earlier Float32Array issue by removing the Router.route path entirely. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 39fb398 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 5173ce7 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit 3c1f701 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (ruvnet#570) * docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities Compares HelixDB (LMDB/heed, compiled type-safe HelixQL, graph-vector thesis, graph-vector-bench) against RuVector's redb/Cypher/hybrid stack and proposes 7 prioritized, opt-in improvements: optional schema layer with load-time validation, first-class typed graph-vector binding and a unified search-then-traverse operator, in-query embed(), unified ANN+BM25+graph RRF hybrid, a reproducible benchmark harness, schema-driven typed SDK codegen, and an object-storage tier research spike. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): native schema layer + typed search-then-traverse (ADR-252 P1/P2/P4) Implements the HelixDB-inspired improvements natively in ruvector-graph: - schema.rs: opt-in GraphSchema (N::/E::/V:: equivalents) with load-time validation (self-consistency, node required/typed props + strict mode, edge from/to label constraints, vector dimension checks), higher-is-better distance metrics (cosine/dot/euclidean), and reciprocal_rank_fusion (P4). - typed_graph.rs: TypedGraph wrapper validating mutations pre-storage, plus a fused typed search_then_traverse operator (HelixQL SearchV<T>(q,k)::In/Out<E>) with optimized bounded-heap top-k selection (O(n log k)). Pure-Rust, no new deps, WASM-safe. 13 new tests, 148/148 lib tests green, clippy clean. Schemaless mode remains the default (opt-in coexistence). https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * perf(ruvector-graph): optimize search_then_traverse + add criterion bench (ADR-252) Hot-path optimizations for the typed search-then-traverse operator: - GraphDB::with_node / node_ids_by_label: zero-copy borrow scoring, eliminating per-candidate Node + embedding clones (get_nodes_by_label cloned everything). - Fused single-pass cosine (q.c and c.c in one read of the candidate) + hoisted query norm out of the per-candidate loop. - Bounded top-k min-heap (O(n log k)); clone id only for heap winners. - Rayon parallel scan over DashMap for >=4096 candidates (per-thread heaps, bounded merge); serial path below threshold. Adds benches/typed_graph_bench.rs (criterion). Measured vs first cut (128-dim, k=10): 10k 7.2ms->3.08ms (2.34x), 50k 74.3ms->28.5ms (2.61x), 1k 539us->432us. New parallel-vs-reference correctness test. 149/149 lib tests green, clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): HNSW push-down for search_then_traverse (ADR-252 P2) Adds an opt-in ANN path to the typed search-then-traverse operator, removing the O(n) full-label scan for indexed vector types: - TypedGraph::build_vector_index(vector_type) builds a per-vector-type HybridIndex (HNSW under hnsw_rs, exact FlatIndex otherwise), holding only the bound label's nodes so searches stay label-scoped. Kept current incrementally via create_node -> index_node. - search_then_traverse routes through the index when present: ~O(log n) approximate search, over-fetch (max(4k, k+32)), then exact rescore with the schema metric so ANN results carry identical higher-is-better score semantics to the brute-force path. Brute force remains the default. - Parallel brute-force path refactored to capture &GraphDB (not &self) so it stays Send+Sync independent of the index's thread-safety bounds. Bench (50k nodes, 128-dim, k=10): brute-force parallel scan 27.6ms -> HNSW push-down 1.05ms (~26x; ~70x vs first cut). 151/151 lib tests green (3 new HNSW tests), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): inline embed() + tri-modal BM25/ANN/graph hybrid (ADR-252 P3/P4) P3 - inline embedding (HelixQL Embed()): - embed.rs: Embedder trait + dependency-free deterministic HashEmbedder (feature-hashing, explicit opt-in, never a silent fallback per ADR-194). - TypedGraph::with_embedder / embed / create_node_from_text (embed-at-insert, dimension-validated) / search_text (embed-at-query). P4 - tri-modal hybrid query: - bm25.rs: self-contained Okapi-BM25 inverted index. - TypedGraph::build_text_index + hybrid_search_text fusing ANN vector + BM25 keyword + graph traversal via reciprocal rank fusion in one typed call. - Refactored search_then_traverse into shared rank_seeds/expand helpers. Bench: hash_embed_256 717ns; tri_modal_hybrid over 10k docs (embed+HNSW+BM25+ RRF+traverse) 1.63ms end-to-end. 164/164 lib tests green (+13), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * feat(ruvector-graph): schema-driven typed SDK codegen (ADR-252 P6) codegen.rs generates typed client stubs from a GraphSchema: - generate_typescript: interfaces with typed/optional properties (@indexed hints), edge from->to constraints, and a VectorTypes manifest + VectorTypeName. - generate_python: TypedDict classes + VECTOR_TYPES manifest. - generate_rust: serde-ready structs. Deterministic (schema elements sorted) for check-in/diff. Adds *_schemas_sorted accessors to GraphSchema. Closes HelixDB's schema->typed-SDK DX advantage. 168/168 lib tests green (+4), clippy clean. https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF * docs(adr): renumber ADR-252 -> ADR-253 (252 taken by FastGRNN training pipeline) ADR-252 was already merged to main as the tiny-dancer FastGRNN training pipeline. Renumber this HelixDB comparison to ADR-253 to resolve the collision. Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 1e1740a Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(mcp-brain-server): use native-tls-vendored so it cross-compiles to aarch64 (ruvnet#573) mcp-brain-server pins reqwest with the `native-tls` feature (for TLS close_notify compatibility). On aarch64 cross-builds, native-tls → openssl-sys fails because the target's OpenSSL dev headers aren't present, so the binary can't be built for Raspberry Pi 5. Switch to `native-tls-vendored`, which builds OpenSSL from source for the target — identical TLS behavior (still native-tls, close_notify compat preserved), just statically linked, no host/target OpenSSL headers required. Context: the Cognitum v0 appliance image (cognitum-one/v0-appliance) builds this binary as `ruview-mcp-brain-mini` (the :9876 vector-memory store) from this repo as a submodule. This is the only blocker preventing it from shipping on the Pi 5 (Hailo-8/10H) appliance. * chore: Update NAPI-RS binaries for all platforms Built from commit e346476 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(ruvector-core): data-loss in update_q_value (ruvnet#562) + silent quantization no-op (ruvnet#563) ruvnet#562 (Critical): PolicyMemoryStore::update_q_value deleted the policy and returned Ok(()) without re-inserting — silent data loss for any RL loop. Now it fetches the entry, updates q_value in metadata, and re-inserts (preserving the embedding); returns VectorNotFound on a missing id instead of false success. Adds a regression test. ruvnet#563 (High): DbOptions.quantization was accepted/persisted but never threaded into the index/storage, while the default was Some(Scalar) and docs promised 4x/32x compression. Default flipped to None; VectorDB::new now warns when a non-None quantization is set; enum/field docs state compression is a target "not yet applied", not a guarantee. 228 ruvector-core lib tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 135a030 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-254 ruvector-turbovec multi-bit FastScan ANN index (ruvnet#520) Canonical ADR for the 2-4-bit scalar-quantized FastScan search index proposed in ruvnet#520 / PR ruvnet#521. Numbered 254 because the PR drafted it as ADR-194, which collides with the merged ADR-194 (ONNX embedder). Captures the gap, the T1-T6 design, reuse boundary, milestones M1-M5, measured M1 validation, and honest divergences from the TurboQuant paper. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit a7028ef Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * docs(adr): ADR-255 ruvector <-> OIA Model integration (alignment profile) Grounded in a deep-research brief over agenticsorg/OIA-Model v0.1: maps OIA's 10 layers (L0-L9) + 6 spans to ruvector components, decides a non-binding alignment profile (ruvector as an L3 + L5-L8 provider), designates the RVF cognitive container as the L8 artifact and the witness chain as the SPAN-AUD/PRV primitive, and explicitly scopes out L0/L1/L9/L4-pretraining + the GCP-portability gap. Stays doc/tag-level — no OIA dependency, no API rename — because OIA is pre-1.0 with no machine-readable conformance. Co-Authored-By: claude-flow <ruv@ruv.net> * chore: Update NAPI-RS binaries for all platforms Built from commit 183ed4a Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(security): SECURITY.md disclosure policy (ruvnet#320) + CORS allowlist (ruvnet#560) (ruvnet#577) - Add SECURITY.md: private disclosure via GitHub PVR or ruv@ruv.net, scope, and response SLAs. Closes the responsible-disclosure gap raised in ruvnet#320 (gives reporters a channel without enabling beg-bounty noise). - mcp-brain-server CORS: add https://app.conceptmapping.org and https://conceptmapping.org to the default allowlist so pi.ruv.io/v1/* returns Access-Control-Allow-Origin for those browser origins (ruvnet#560). Kept an explicit per-origin allowlist (not `*`) since callers authenticate with Bearer tokens. cargo check -p mcp-brain-server: clean. Refs ruvnet#320 ruvnet#560 Co-authored-by: ruv <ruvnet@users.noreply.github.com> * ADR-256: harness router surface (borrow metaharness concepts) (ruvnet#575) * feat(ruvector): ADR-256 harness router surface + tracking (ruvnet#574) Borrow metaharness concepts using primitives ruvector already ships. - Add `ruvector harness status [--json]` — unified read-only view of the routing surface (Tiny Dancer cost router + semantic router + hooks routing + MCP + witness + memory), degrading gracefully when optional deps are absent. Implements ADR-256 rollout step 0. - Add ADR-256 (borrow-concepts decision, concept→primitive mapping). - Add CLI tests (Section 24): harness --help, status --json structure, bare-command behavior. Full suite: 72 passed, 0 failed. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * feat(ruvector): ADR-256 default-deny MCP tool-access policy (ruvnet#574) Borrow metaharness's default-deny allowlist concept with our own machinery. - New pure, testable bin/mcp-policy.js: RUVECTOR_MCP_ALLOW / RUVECTOR_MCP_DENY / RUVECTOR_MCP_PROFILE=readonly. Precedence DENY > ALLOW/PROFILE > allow-all. No policy set = backward-compatible allow-all (policy.configured=false). - Wire into mcp-server.js: ListTools now returns only permitted tools; CallTool gates denied tools with an isError response before dispatch. - harness status --json now reports mcp.policy + accessControl posture. - Tests: test/mcp-policy.js (8 unit tests) wired into npm test; verified end-to-end over MCP stdio (readonly profile exposes 10 safe tools, filters hooks_force_learn). CLI suite still 72/0. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * test(ruvector): ADR-256 startup-budget guard + harness/MCP-policy docs (ruvnet#574) - New test/startup-budget.js wired into npm test: absolute ceiling on `--help` cold start + relative delta guard ensuring `harness status` adds < 120ms over baseline (catches a heavy module leaking into the startup path). Measured here: --help 127ms, harness +3ms. Env-overridable. - README: document the default-deny MCP policy env vars (RUVECTOR_MCP_ALLOW/DENY/PROFILE) and the `harness` router command. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> * feat(ruvector): ADR-256 memory namespace + full verification, ADR status (ruvnet#574) - harness surface reports a stable memory namespace (RUVECTOR_MEMORY_NAMESPACE, default `ruvector`); CLI tests assert the default + override and the MCP accessControl/policy fields. - README documents the memory namespace. - ADR-256: add "Implementation status (as shipped)" — items 0/1/3/4 done, benchmarked + full npm test green; item 2 as a documented convention; item 5 deferred. No @metaharness/* runtime dep. Full suite: cli 73/0, mcp-policy 8/0, startup-budget 2/0, db-workflow/integration/sigterm green. Refs ruvnet#574 Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore(ruvector): bump to 0.2.32 (ADR-256 release prep) (ruvnet#578) * chore(ruvector): bump to 0.2.32 (harness router + default-deny MCP policy) Release prep for the ADR-256 work merged in ruvnet#575: - `ruvector harness status [--json]` unified routing surface - default-deny MCP tool policy (RUVECTOR_MCP_ALLOW/DENY/PROFILE) - stable memory namespace (RUVECTOR_MEMORY_NAMESPACE) - startup-budget CI guard Additive / non-breaking → patch bump. Tests green (73/0, 8/0, 2/0). Does NOT publish — `npm publish` left to a maintainer with npm auth. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(ruvector): cross-platform build script (node fs.cpSync) The build copied ONNX assets with `mkdir -p`/`cp -r`, which fail on Windows cmd.exe and broke `npm publish` (prepack/prepublishOnly) off-Linux. Replace with node fs.cpSync(recursive) — works everywhere. Verified: build + verify-dist green on Windows (14 dist paths present). Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: ruv <ruvnet@users.noreply.github.com> * chore: Update NAPI-RS binaries for all platforms Built from commit 716dbed Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * chore: Update NAPI-RS binaries for all platforms Built from commit a2c5ce4 Platforms updated: - linux-x64-gnu - linux-arm64-gnu - darwin-x64 - darwin-arm64 - win32-x64-msvc 🤖 Generated by GitHub Actions * fix(wasm): unblock wasm bridge builds (getrandom/uuid js, wasm-opt=false) Apply RuVector-canonical wasm fixes (verified vs passing -wasm crates): - wasm-opt = false for bulk-memory validator failures (delta, fpga-transformer, graph-condense, mincut-gated-transformer, solver, sparse-inference) - getrandom { features=[js] } + uuid { features=[js] } for wasm32 (router, tiny-dancer) Result: 24 -> 29 of 35 -wasm crates build. Remaining 6 have distinct deeper causes (sqlite-C not wasm-able, arithmetic_overflow lint, non-member crates). Co-Authored-By: claude-flow <ruv@ruv.net> * fix(wasm): round 2 — mincut overflow, sqlite/simsimd gating, more js/wasm-opt (29→33/35) - mincut-gated-transformer: 16GB usize literal overflowed wasm32 32-bit usize -> saturating_mul (16GB on 64-bit, caps at usize::MAX on wasm32). PRIMARY crate. - domain-expansion-wasm: getrandom js feature - temporal-tensor-wasm: add to workspace.members + wasm-opt=false - tiny-dancer-core: cfg(not(wasm32))-gate native-only C deps — rusqlite (bundled SQLite) + simsimd (C SIMD); add scalar cosine fallback for wasm32. Native unchanged. Result: 33/35 -wasm crates build. Remaining 2: sparse-inference-wasm (lib.rs API drift vs SparseModel — needs wrapper update), micro-hnsw-wasm (excluded stub). Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: rUv <ruv@ruv.net> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: ruv <ruvnet@users.noreply.github.com> Co-authored-by: Dragan Spiridonov <spiridonovdragan@gmail.com>
What & why
Adds
crates/ruvector-turbovec— a multi-bit TurboQuant FastScan-style ANN index — and ADR-194 documenting it. This closes the one mainstream ANN regime ruvector lacks: 2–4-bit scalar-quantized search (FAISSIndexPQFastScan/ Qdrant / Milvus IVF-SQ territory).Companion issue with the full rationale: #520.
The gap this fills
ruvector-rabitqruvllmturbo_quant.rsAnnIndex(no top-k / LUT kernel / IDs)ruvector-turbovec(new)Techniques adapted from RyanCodrai/turbovec (Google Research TurboQuant, arXiv:2504.19874). Clean-room crate, not a port — it reuses ruvector's own primitives rather than duplicating them.
Why it's a good fit for this repo
ruvector_rabitq::AnnIndextrait and reusesruvector_rabitq::RandomRotation(the randomized Hadamard, already cited to the same arXiv paper inrotation.rs). Drops into the same consumers as RaBitQ.c_xmakes the inner-product estimator unbiased at zero query-time cost (it folds into the final multiply), so the memory win is real.rairs-ivf).Pipeline (ADR-194 §T1–T4, T6)
Plus
IdMapIndex: externalu64ids, O(1) delete (tombstone), allowlist-filtered search (§T6).Measured proof —
cargo run --release -p ruvector-turbovecn = 5,000uniform-random vectors (the worst case for ANN — no cluster structure),dim = 256,k = 10, no f32 rerank, scored vs exact brute-force L2:c_xrenormalization yields an unbiased estimator (the core TurboQuant claim).Tests / quality
D_mse ≤ (√3·π/2)·4^(−b)and within 5% of the Max-1960 optimum, every width).cargo build --release -p ruvector-turbovecgreen; clippy clean;cargo fmt --checkclean.unsafein M1.Post-review hardening (commits 5–9)
After the initial push the branch was tightened — each as its own focused commit:
BitWidth::Three(8-level Max-1960 levels) fills the 2↔4-bit recall cliff: 0.767 @ 9.8×, measured. The bit-packer was already width-generic, so this is a centroid table + enum arms.cargo fmt— isolated, normalizes pre-existing >100-col lines.ADR-194 gained a "Divergences from the TurboQuant paper (arXiv:2504.19874)" section (D1–D5): where M1 departs from the paper and why. The notable one (D1): M1 scores with a per-vector
c_xlength-renormalization — empirically near-unbiased — rather than the paper's provably unbiased two-stage MSE + 1-bit-QJL-residual estimator. That's a deliberate cost/accuracy trade (zero extra bits vsmbits/vector); the QJL stage is scoped as a follow-up (M5), designed in #520.Scope / follow-ups
This is M1 (scalar reference), now shipping 1 / 2 / 3 / 4-bit. The scalar scorer is intentionally the determinism oracle for later SIMD work:
.tvpersistence.ruvector-rulakedispatcher registration.d-aware Beta-optimal codebooks (§D2).Files
docs/adr/ADR-194-ruvector-turbovec-fastscan-index.md— full design, reuse boundaries, alternatives, acceptance criteria, measured results.crates/ruvector-turbovec/—quantize.rs,calibrate.rs,index.rs,idmap.rs,error.rs,lib.rs,main.rs(proof demo).Cargo.toml/Cargo.lock— workspace member registration.