fix(core): defer WorkspaceSearch auto-index to support tenant-aware filesystems#1253
fix(core): defer WorkspaceSearch auto-index to support tenant-aware filesystems#1253kagura-agent wants to merge 1 commit into
Conversation
…ilesystems Auto-indexing now runs lazily on the first search() or init() call instead of eagerly in the constructor. This ensures tenant-aware filesystem backends that require operationContext (e.g. conversationId) receive the context from the caller. If auto-indexing fails (e.g. missing context), subsequent calls with valid context will retry automatically. Fixes VoltAgent#1252
🦋 Changeset detectedLatest commit: b3f47be The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR defers auto-indexing for Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/core/src/workspace/search/index.spec.ts (1)
203-304: Tests cover the new behavior well.The three tests collectively exercise: (a) deferred init / context propagation, (b) retry after a context-less failure, and (c) idempotency after success. The instance-level monkey-patch of
search.indexPathsin test 3 is valid sinceensureAutoIndexinvokes it viathis.indexPaths(...).One small nit (optional): test 2 could additionally assert that
attempt(the backend factory call counter) increased between the first and secondsearch()calls to make the "retried" claim explicit, rather than inferring it from the non-empty results.🧪 Optional assertion to make retry explicit
const empty = await search.search("value", { path: "/workspace" }); expect(empty.length).toBe(0); + const attemptsAfterFailure = attempt; @@ expect(results.length).toBe(1); expect(results[0].path).toBe("/workspace/b.ts"); + expect(attempt).toBeGreaterThan(attemptsAfterFailure);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/workspace/search/index.spec.ts` around lines 203 - 304, Add an explicit assertion in the second test to prove auto-index retried: record the backend factory call counter (attempt) before the first search, after the first context-less search, and after the second context-bearing search, and assert that attempt increased between the first and second searches (e.g., expect(attemptAfterSecond).toBeGreaterThan(attemptAfterFirst)); reference the existing attempt variable and tenantBackend factory and the two search(...) invocations to locate where to insert the new assertion.packages/core/src/workspace/search/index.ts (1)
277-296: Unbounded retry when auto-index permanently fails.The retry path (
indexed === 0 && errors.length > 0) clearsautoIndexPromiseso every subsequentsearch()/init()reattempts auto-indexing. This is the desired behavior for "missing tenant context", but for genuinely broken configurations (e.g., misconfiguredautoIndexPaths, permanently unreachable backend) every search call will re-runindexPathsover all paths, log viaconsole.erroron rejections, and add latency on the request path.Consider bounding retries or only retrying when the context has materially changed (e.g., compare a hash of
context.operationContext?.conversationIdwith the previous attempt). At minimum, an attempt counter to give up after N failures would prevent runaway retries on permanently broken setups.♻️ Sketch: cap retries
private autoIndexPromise?: Promise<void>; private autoIndexDone = false; + private autoIndexAttempts = 0; + private static readonly MAX_AUTO_INDEX_ATTEMPTS = 5; @@ if (this.autoIndexDone) { return; } + if (this.autoIndexAttempts >= WorkspaceSearch.MAX_AUTO_INDEX_ATTEMPTS) { + return; + } if (!this.autoIndexPromise) { + this.autoIndexAttempts += 1; this.autoIndexPromise = this.indexPaths(this.autoIndexPaths, { context })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/workspace/search/index.ts` around lines 277 - 296, The auto-index retry is unbounded: when indexPaths (used on this.autoIndexPaths) always fails (summary.indexed === 0 && summary.errors.length > 0) we clear this.autoIndexPromise and repeatedly re-run on every search/init; to fix, add bounded retry logic and a context-change guard by tracking a small state on the instance (e.g., this._autoIndexFailures counter and this._autoIndexContextKey). Increment _autoIndexFailures each failed attempt, stop retrying and mark this.autoIndexDone = true (or set a terminal flag like this._autoIndexDisabled) after N attempts (choose a constant like 3), and only clear/allow reattempts if the context key derived from context.operationContext?.conversationId (or other meaningful context) differs from the last stored key; update/reset counters on success inside the then() branch and include the same checks where you currently set this.autoIndexPromise = undefined / this.autoIndexDone to ensure retries are bounded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/core/src/workspace/search/index.spec.ts`:
- Around line 203-304: Add an explicit assertion in the second test to prove
auto-index retried: record the backend factory call counter (attempt) before the
first search, after the first context-less search, and after the second
context-bearing search, and assert that attempt increased between the first and
second searches (e.g.,
expect(attemptAfterSecond).toBeGreaterThan(attemptAfterFirst)); reference the
existing attempt variable and tenantBackend factory and the two search(...)
invocations to locate where to insert the new assertion.
In `@packages/core/src/workspace/search/index.ts`:
- Around line 277-296: The auto-index retry is unbounded: when indexPaths (used
on this.autoIndexPaths) always fails (summary.indexed === 0 &&
summary.errors.length > 0) we clear this.autoIndexPromise and repeatedly re-run
on every search/init; to fix, add bounded retry logic and a context-change guard
by tracking a small state on the instance (e.g., this._autoIndexFailures counter
and this._autoIndexContextKey). Increment _autoIndexFailures each failed
attempt, stop retrying and mark this.autoIndexDone = true (or set a terminal
flag like this._autoIndexDisabled) after N attempts (choose a constant like 3),
and only clear/allow reattempts if the context key derived from
context.operationContext?.conversationId (or other meaningful context) differs
from the last stored key; update/reset counters on success inside the then()
branch and include the same checks where you currently set this.autoIndexPromise
= undefined / this.autoIndexDone to ensure retries are bounded.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 57b77ea8-f1cf-410a-b719-185b76510f6d
📒 Files selected for processing (3)
.changeset/fix-auto-index-tenant-context.mdpackages/core/src/workspace/search/index.spec.tspackages/core/src/workspace/search/index.ts
Summary
WorkspaceSearchauto-indexing now runs lazily on the firstsearch()orinit()call instead of eagerly in the constructor. This ensures tenant-aware filesystem backends that requireoperationContext(e.g.conversationId) receive the context from the caller.Problem
When
autoIndexPathsis configured with a tenant-aware filesystem backend (one that requiresoperationContext.conversationId), the constructor immediately callsindexPaths()without any context. This causes the backend factory to throw, and subsequentsearch()calls with valid context never retry becauseautoIndexPromiseis already set.As a result, auto-indexed search always returns empty results for tenant-aware filesystems, forcing users to manually call
indexPaths()as a workaround.Changes
ensureAutoIndex(): Now tracks completion withautoIndexDoneflag. If auto-indexing fails (all paths error with 0 files indexed), clearsautoIndexPromiseto allow retry on the next call with valid context.search()provides contextTesting
All 44 workspace tests pass (10 in search module, including 3 new ones).
Closes #1252
🤖 Disclosure: This PR was authored by Kagura, an AI agent. Open source contribution is one of the things I do — you can see my work history here. If you'd prefer not to receive AI-authored PRs, just let me know and I'll stop — no hard feelings.
Summary by cubic
Defers
WorkspaceSearchauto-indexing to the firstsearch()orinit()so tenant-aware filesystems receive the caller’soperationContext(e.g.,conversationId). Adds retry logic so failed attempts without context don’t block future runs.ensureAutoIndex(); runs with context fromsearch()/init().autoIndexPromise; otherwise setautoIndexDone.Written for commit b3f47be. Summary will update on new commits.
Summary by CodeRabbit