Files
gbrain/test/search-alias-resolved-boost.test.ts
85286a556c fix(search): stop boosting compiled_truth at default detail (#3430) (#3514)
* fix(search): stop boosting compiled_truth at default detail (#3430)

COMPILED_TRUTH_BOOST = 2.0 is applied AFTER RRF normalization, and RRF's whole
dynamic range over a 100-deep pool is 1/60 -> 1/160 (a factor of 2.67). So a
2.0x multiplier consumes roughly three quarters of the range: break-even is
`2/(60+r) >= 1/60`, i.e. r <= 60, which means ANY boosted chunk inside the
first 60 ranks outranks an unboosted rank-1 chunk. That is a categorical
filter, not a tilt.

Measured against master's own rrfFusion, with the correct answer in a
fenced_code chunk at vector rank 0:

  compiled_truth chunks in pool | final rank | in top-20
  10                            | 10         | yes
  20                            | 20         | NO
  40                            | 40         | NO
  80                            | 59         | NO

With the boost off the answer stays at rank 0 in every case.

The gate was spelled `detail !== 'high'` -- written as though `high` were the
special case. The documented contract in src/core/operations.ts is
"low (compiled truth only), medium (default, all with dedup), high (all
chunks)", which makes LOW the special one: `low` already restricts to
compiled_truth, so a boost there is a no-op among equals, while `medium` and
`high` are both meant to see everything. So the default detail was silently
compiled-truth-only, contradicting the op's own description.

Three changes:

1. The three fusion call sites now route through a named predicate,
   `shouldBoostCompiledTruth(detail)`, returning true only for 'low'.
   Extracted rather than left inline precisely because an inline expression is
   only reachable through a full hybridSearch round trip -- which is why the
   inversion went unnoticed. The predicate is directly unit-testable.

2. KNOBS_HASH_VERSION 13 -> 14. Results are cached AFTER fusion, so rows
   ranked under the old semantics would otherwise be served under the new ones
   for the whole TTL (3600s default). One-time miss spike on upgrade.

3. test/search-compiled-truth-boost-scope.test.ts pins both the mapping and
   the arithmetic, and documents the displacement it prevents.

Verified the tests discriminate: stubbing the OLD predicate body into master
(so the failure is behavioral rather than a missing export) gives 4 fail /
3 pass; with the fix, 7 pass. typecheck clean, verify 32/32, and 144 pass /
0 fail across the search + fusion + cache suites.

* fix(test): update the three remaining KNOBS_HASH_VERSION pins to 14 (#3430)

Missed in the first pass because I ran a targeted set of test files instead of
the full suite. CI shards 3, 8 and 10 caught them:

  test/search/knobs-hash-reranker.test.ts:67
  test/cross-modal-phase1.test.ts:139,149
  test/search-alias-resolved-boost.test.ts:93

Each carries the running history of why the version moved, so each gets the
13→14 rationale appended rather than just the number swapped. No pins at 13
remain anywhere in test/.

---------

Co-authored-by: Garry Tan <garrytan@gmail.com>
2026-07-28 19:27:46 -07:00

96 lines
3.8 KiB
TypeScript

// v0.42 Type Unification (T32) — alias_resolved search boost stage.
//
// Coverage: pages whose slug is a canonical_slug in slug_aliases get 1.05x
// score multiplier; non-alias-canonical pages unchanged; stage stamps
// alias_resolved_boost field for --explain; KNOBS_HASH_VERSION bumped.
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
import { resetPgliteState } from './helpers/reset-pglite.ts';
import { runPostFusionStages, type PostFusionOpts } from '../src/core/search/hybrid.ts';
import { KNOBS_HASH_VERSION } from '../src/core/search/mode.ts';
import type { SearchResult } from '../src/core/types.ts';
let engine: PGLiteEngine;
beforeAll(async () => {
engine = new PGLiteEngine();
await engine.connect({});
await engine.initSchema();
});
afterAll(async () => {
await engine.disconnect();
});
beforeEach(async () => {
await resetPgliteState(engine);
});
const noopPostFusionOpts: PostFusionOpts = {
applyBacklinks: false,
salience: 'off',
recency: 'off',
graphSignalsEnabled: false,
};
describe('alias_resolved boost stage', () => {
it('applies 1.05x multiplier to pages that are canonicals of aliases', async () => {
// Insert an alias pointing at canonical-page
await engine.executeRaw(
`INSERT INTO slug_aliases (source_id, alias_slug, canonical_slug) VALUES ('default', 'old-name', 'canonical-page')`,
);
const results: SearchResult[] = [
{
slug: 'canonical-page', source_id: 'default', score: 1.0,
chunk_id: 1, page_id: 1, chunk_text: '', chunk_index: 0,
title: 'Canonical', type: 'concept' as never, slug_lower: 'canonical-page',
} as unknown as SearchResult,
{
slug: 'plain-page', source_id: 'default', score: 1.0,
chunk_id: 2, page_id: 2, chunk_text: '', chunk_index: 0,
title: 'Plain', type: 'concept' as never, slug_lower: 'plain-page',
} as unknown as SearchResult,
];
await runPostFusionStages(engine, results, noopPostFusionOpts);
// canonical-page gets 1.05x boost
expect(results[0].score).toBeCloseTo(1.05, 5);
expect(results[0].alias_resolved_boost).toBe(1.05);
// plain-page unchanged
expect(results[1].score).toBeCloseTo(1.0, 5);
expect(results[1].alias_resolved_boost).toBeUndefined();
});
it('does not boost when no aliases exist', async () => {
const results: SearchResult[] = [{
slug: 'plain', source_id: 'default', score: 1.0,
chunk_id: 1, page_id: 1, chunk_text: '', chunk_index: 0,
title: 'p', type: 'concept' as never, slug_lower: 'plain',
} as unknown as SearchResult];
await runPostFusionStages(engine, results, noopPostFusionOpts);
expect(results[0].score).toBeCloseTo(1.0, 5);
expect(results[0].alias_resolved_boost).toBeUndefined();
});
it('is source-scoped (F9): alias in source A does not boost in source B', async () => {
await engine.executeRaw(`INSERT INTO sources (id, name) VALUES ('alt', 'alt') ON CONFLICT DO NOTHING`);
await engine.executeRaw(
`INSERT INTO slug_aliases (source_id, alias_slug, canonical_slug) VALUES ('alt', 'old', 'shared')`,
);
// Same slug, different source — should NOT be boosted (alias is in 'alt')
const results: SearchResult[] = [{
slug: 'shared', source_id: 'default', score: 1.0,
chunk_id: 1, page_id: 1, chunk_text: '', chunk_index: 0,
title: 's', type: 'concept' as never, slug_lower: 'shared',
} as unknown as SearchResult];
await runPostFusionStages(engine, results, noopPostFusionOpts);
expect(results[0].alias_resolved_boost).toBeUndefined();
});
});
describe('KNOBS_HASH_VERSION', () => {
it('is 14 (13→14 compiled_truth boost no longer applies at detail=medium, so pre-fix rankings must be unreachable, #3430)', () => {
expect(KNOBS_HASH_VERSION).toBe(14);
});
});