fix(hybrid): propagate SearchOpts filters to inner keyword/vector searches#282
Open
x64Nika wants to merge 1 commit intogarrytan:masterfrom
Open
fix(hybrid): propagate SearchOpts filters to inner keyword/vector searches#282x64Nika wants to merge 1 commit intogarrytan:masterfrom
x64Nika wants to merge 1 commit intogarrytan:masterfrom
Conversation
…rches
hybridSearch() constructed its inner searchOpts with only { limit, detail },
silently dropping caller-provided `type` and `exclude_slugs`. Any caller using
hybridSearch(engine, query, { type: 'person' }) got unfiltered results while
believing the filter was applied.
Fix: spread caller opts before overriding limit/offset/detail. This preserves
every filter field (current and future) without individual plumbing per field.
Also adds regression tests covering type, exclude_slugs, limit/offset override,
and undefined-opts cases.
x64Nika
added a commit
to x64Nika/gbrain
that referenced
this pull request
Apr 22, 2026
Add `gbrain doctor --strict` mode with two extra check families: 1. Typed-edge bidirectionality — for configured inverse pairs like refutes↔refuted_by, verify every forward edge has its reciprocal. Default pair list: refutes↔refuted_by. Extend INVERSE_EDGE_PAIRS in src/commands/doctor-strict.ts to add new pairs. 2. Tag-prefix uniqueness — for user-configured prefixes (set via env GBRAIN_DOCTOR_TAG_PREFIXES=stance-,priority-), verify no page carries >1 tag matching the prefix. Empty env = check skipped. Both checks emit WARN not FAIL — drift is content-side and doesn't break the runtime. FAIL stays reserved for infra breakage. Files: - src/commands/doctor-strict.ts (new, ~110 LOC) - src/commands/doctor.ts (+18 LOC conditional invocation) - src/cli.ts (+1 help line) - test/doctor-strict.test.ts (new, 6 tests gated on DATABASE_URL) Tests: - bun test: 1756 pass, 0 fail (0 regression) - DATABASE_URL=... bun test test/doctor-strict.test.ts: 6/6 pass - Live smoke: gbrain doctor --strict runs and adds 2 OK checks Upstream PR track: both check families are generic (inverse-pair graph invariants, tag-prefix uniqueness via env config). Good candidates for upstream PR after Patch 0 (garrytan#282) lands — provides value to any gbrain user with typed edges or tag naming conventions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hybridSearch()silently drops caller-providedtypeandexclude_slugsfilter options when buildingsearchOptsfor innersearchKeyword/searchVectorcalls:https://github.com/garrytan/gbrain/blob/master/src/core/search/hybrid.ts#L71
Any caller using
hybridSearch(engine, query, { type: 'person' })or{ exclude_slugs: [...] }gets unfiltered results while believing the filter is applied — the outerhybridSearchnever reads those fields again, and the inner searches never see them.Fix
One-line spread before overriding
limit/offset/detail:This preserves every caller-provided filter field (current and future — any field added to
SearchOptsbecomes automatically propagated, no additional plumbing).offsetis explicitly set to0on the inner searches because the outerhybridSearchslices the final result set at[offset, offset+limit]itself — propagating the caller's offset into the inner searches would double-apply it.Test plan
Added
test/hybrid-search-opts.test.tswith 4 regression tests covering:typefilter propagates tosearchKeywordexclude_slugspropagateslimitis overridden toinnerLimit(outerlimit * 2) andoffsetis forced to0undefinedopts don't throwAll existing tests still pass (
bun test test/search.test.ts test/search-limit.test.ts— 42 pass, 0 fail).Happy to add similar tests on the vector path or integration-level tests if that would help. Also flagged as draft in case you'd prefer a different fix shape (e.g., explicit per-field spread instead of full
...opts).