Loop inputs:
initialK: first attempt's k (typically max(overFetch * 3, 50))maxLabels: index size; k is capped at thislimit: caller's desired top-K (loop stops once filtered ≥ limit)searchKnn(k): HNSW search returning labels + distances arraysfilter(labels, distances): apply min_score / folder / privacy
filters; returns a post-filtered hit arraymaxAttempts: bound on iterations (default 3)the final filtered hit array (may still be < limit if even the saturated k+filter couldn't satisfy — caller must handle).
v3.9.0-rc.3 R-10 — adaptive HNSW refill loop for under-returned semantic-search queries.
Background: the embed-db can contain entries for paths that the privacy filter (
vault.isExcluded) then drops at response-build time. Pre-3.9.0-rc.3:limit * 2from HNSW.max(overFetch * 3, 50)(effective 6× limit).The adaptive loop solves this self-tuningly: if after filtering the result set is < limit AND k < maxLabels, double k and try again. Bounded by
maxAttempts(default 3) so a fully-exhausted query doesn't burn arbitrary CPU. Most vaults converge on the first attempt (typical exclude ratio < 20%); the refill engages only for long-tail privacy-heavy configurations.Pure function. The HNSW search and filter callbacks are injected so tests can drive the loop with stub searchKnn + filter implementations.