Files
gbrain/test/cache-scope-key.test.ts
T
Garry TanandClaude Opus 4.8 78d2d3dc18 feat(search): relational recall arm + federation key hardening
Wires edge-derived candidates into bare hybridSearch as a FOURTH RRF arm
(relational-recall.ts): parse the original query -> scope-aware,
confidence-gated seed resolution (drops fallback_slugify; never traverses
from a guess) -> relationalFanout -> batch-hydrate, reinforcing each page's
REAL canonical chunk (page-level key for chunkless entity pages) ->
--explain attribution + fail-open audit row. Text-only (no-op in image
mode); pure no-op for non-relational queries; rides every downstream stage
(cosine, post-fusion boosts, dedup, reranker, autocut, token budget).

Mode wiring: relationalRetrieval + relational_retrieval_depth knobs
(conservative off; balanced/tokenmax on; depth 2), per-call thread-through
in both bare + cached paths, KNOBS_HASH_VERSION 9->10 (rel=/reld=), config
keys, modes-dashboard descriptions, and a `relational` param on the query op.

Federation hardening (structural, engine-wide): the RRF/dedup key now
carries source_id via a shared rrfKey() (fixes a latent cross-source
collapse where same-slug pages in different sources merged), and the query
cache scopes by a canonical source-set key (cacheScopeKey) so a federated
read can't be served a single-source row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:08:51 -07:00

43 lines
1.6 KiB
TypeScript

/**
* Query-cache scope key (federation hardening).
*
* A federated search reads a different graph than a single-source one, so
* the semantic cache must key them apart. `cacheScopeKey` produces an
* order-independent key for federated scopes and leaves single-source
* brains on their existing key (scalar id or 'default'), so single-source
* cache hit-rate is unchanged.
*/
import { describe, test, expect } from 'bun:test';
import { cacheScopeKey } from '../src/core/search/hybrid.ts';
describe('cacheScopeKey', () => {
test('unscoped → default (single-source unchanged)', () => {
expect(cacheScopeKey(undefined)).toBe('default');
expect(cacheScopeKey({})).toBe('default');
});
test('scalar sourceId → itself (single-source unchanged)', () => {
expect(cacheScopeKey({ sourceId: 'host' })).toBe('host');
});
test('federated sourceIds → order-independent set key', () => {
const k1 = cacheScopeKey({ sourceIds: ['team-b', 'team-a', 'host'] });
const k2 = cacheScopeKey({ sourceIds: ['host', 'team-a', 'team-b'] });
expect(k1).toBe(k2); // order does not matter
expect(k1).toBe('__set__:host,team-a,team-b');
});
test('different source-sets do NOT share a key', () => {
const a = cacheScopeKey({ sourceIds: ['host', 'team-a'] });
const b = cacheScopeKey({ sourceIds: ['host', 'team-b'] });
expect(a).not.toBe(b);
});
test('federated set key is distinct from any single scalar key', () => {
const set = cacheScopeKey({ sourceIds: ['host'] });
const scalar = cacheScopeKey({ sourceId: 'host' });
expect(set).not.toBe(scalar); // a 1-element set still cannot serve a scalar read
});
});