fix(context): recover semantic injection when scoped Chroma metadata misses matches (closes #2979)#3011
Conversation
Greptile SummaryRestores the public Keeps the widened semantic hydration window internal to Ignores public Preserves relevance ordering by applying limits after Chroma ID order restoration. Redacts raw semantic query text from Chroma failure logs by recording query length instead. Confidence Score: 5/5The changes are narrowly scoped to semantic search limits, internal hydration behavior, and response shaping, with no review comments requiring attention. The implementation aligns with the described contract: public search results remain limited while internal semantic context recovery can keep a bounded widened window, and the changed behavior is covered by targeted tests plus build and lint checks.
What T-Rex did
Reviews (17): Last reviewed commit: "fix(search): keep unscoped semantic cont..." | Re-trigger Greptile |
50fbc1e to
a62ae10
Compare
6f29aad to
64575bc
Compare
|
Follow-up pushed in The previous follow-up restored the public response-size contract, but it also let the caller-facing That keeps the unbounded-input concern closed as well. Any internal override still clamps back to
|
…misses matches (closes thedotmack#2979)
38b7fe3 to
cca5052
Compare
Summary
Semantic injection could still recover the right project-scoped context after the earlier route fix, but the widened hydration window escaped into general
SearchManager.search()callers. A normal/api/search?...&type=observations&limit=5&format=jsonsemantic read could therefore hydrate up to the Chroma batch ceiling instead of the requested limit, and a caller could also pass an arbitrarily largesemanticLimitquery param through the public search surface.This update keeps the widened window bounded and internal without collapsing the semantic candidate set.
SearchManager.search()now ignores publicsemanticLimitargs, uses a boundedCHROMA_BATCH_SIZEsemantic candidate and hydration window for Chroma observation searches, clamps any internal override to that same ceiling, and slices public JSON observation responses back to the caller-facinglimitunless the semantic-context route explicitly asks to keep the wider result set for its own merge step. That preserves the semantic-context recovery behavior, keeps downstream recency and SQLite filters working over a wide semantic window, restores the public/api/searchlimit contract, and removes the unbounded public input.Thanks to @amellman for the reproduction details in #2979.
Why
src/cli/handlers/session-init.ts:135-141still POSTs{ q, project, limit }to/api/context/semantic, andsrc/services/worker/http/routes/SearchRoutes.ts:464-513still needs a wider semantic window to recover adopted rows and preserve global relevance when the project-scoped hydration path is incomplete.The regression was in
src/services/worker/SearchManager.ts:306-505. The prior head interpretedsemanticLimitas part of the normalized public search args, defaulted it toCHROMA_BATCH_SIZE, and then reused that widened value for observation hydration on every semantic search. The first follow-up removed the public input, but it also made the caller-facinglimitdouble as the Chroma candidate window for ordinary semantic searches. That restored the response-size contract, but it could starve the later recency and SQLite-side concept or file filters by truncating the semantic id set before those filters ran.The fix keeps the widening internal without shrinking the semantic candidate set.
src/services/worker/http/routes/SearchRoutes.ts:496-513still passes{ semanticHydrationLimit: CHROMA_BATCH_SIZE }on the semantic-context route's scoped and unscoped recovery reads so that route keeps the full widened observation set for its own ranking merge.src/services/worker/SearchManager.ts:306-315now ignores publicsemanticLimit, falls back toCHROMA_BATCH_SIZEfor the semantic candidate and hydration window when no internal override is supplied, and clamps any internal override to the same ceiling beforequeryChroma(...)andgetObservationsByIds(...)use it.src/services/worker/SearchManager.ts:498-505then slices public JSON observation arrays back to the requestedlimitunless the internal caller explicitly asked to preserve the wider window.Scope
This does not change the semantic-context route's bounded two-pass recovery design, fallback ordering, or prompt-redaction behavior.
This does not change
session-init,ChromaSync, or the general/api/searchranking logic beyond restoring the publiclimitcontract while keeping the bounded semantic candidate window intact for observation searches.This does not add a new public search parameter. The wider semantic window is now internal-only again.
Risk
The behavior change is narrow: only the
SearchManager.search()execution contract, its JSON response shaping for observation searches, and the semantic-context route call sites changed. The main preserved invariants are that/api/context/semanticstill gets the widened bounded semantic window it needs for adopted-row recovery, while public JSON observation searches stay on their requested response size and still evaluate recency and SQLite-side filters against a wide semantic candidate set. The remaining risk is limited to internal callers that might want a widened result array in JSON mode in the future, in which case they now have to opt in explicitly through the internal execution option.Verification
bun test tests/worker/http/routes/search-routes-semantic-context.test.ts tests/worker/SearchManager.semantic-limit.test.tsnpm run buildnpm run lint:hook-ionpm run lint:spawn-envnpm run strip-comments:check— existing repo baseline noise,Changed: 329Closes #2979