@cue-dev/retrieval-core 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,815 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { SqliteIndexRepository, SqliteQueryCache } from "@cue-dev/data-plane";
6
+ import { RetrievalCore, mergeRetrievalScoringConfig, resolveRetrievalScoringProfile } from "../src/index.js";
7
+
8
+ const QUALITY_THRESHOLD = 0.75;
9
+ const P95_LATENCY_THRESHOLD_MS = 500;
10
+
11
+ function percentile(values: number[], p: number): number {
12
+ if (values.length === 0) {
13
+ return 0;
14
+ }
15
+ const sorted = [...values].sort((a, b) => a - b);
16
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1));
17
+ return sorted[idx] ?? 0;
18
+ }
19
+
20
+ describe("retrieval benchmark thresholds", () => {
21
+ const dirs: string[] = [];
22
+
23
+ afterEach(async () => {
24
+ while (dirs.length > 0) {
25
+ const dir = dirs.pop();
26
+ if (dir) {
27
+ await rm(dir, { recursive: true, force: true });
28
+ }
29
+ }
30
+ });
31
+
32
+ it("uses repository ranking path when available", async () => {
33
+ const root = await mkdtemp(join(tmpdir(), "cue-rank-path-"));
34
+ dirs.push(root);
35
+ const sqlitePath = join(root, "bench.sqlite");
36
+
37
+ const repo = new SqliteIndexRepository(sqlitePath);
38
+ await repo.migrate();
39
+ await repo.upsertWorkspace({
40
+ workspace_id: "ws-bench",
41
+ tenant_id: "tenant-bench",
42
+ name: "bench",
43
+ project_root_path: "/workspace/bench"
44
+ });
45
+
46
+ const cache = new SqliteQueryCache(sqlitePath);
47
+ const core = new RetrievalCore(repo, cache);
48
+
49
+ try {
50
+ await core.indexArtifact({
51
+ tenant_id: "tenant-bench",
52
+ workspace_id: "ws-bench",
53
+ index_version: "idx-rank",
54
+ files: [
55
+ {
56
+ path: "src/security/auth.ts",
57
+ language: "typescript",
58
+ content:
59
+ "export function requireTenant(id: string) {\n if (!id) throw new Error('missing tenant');\n return id;\n}\n"
60
+ }
61
+ ]
62
+ });
63
+
64
+ // Guardrail: if search falls back to in-memory list mode, this test should fail.
65
+ const originalList = repo.listChunksByIndex.bind(repo);
66
+ repo.listChunksByIndex = async () => {
67
+ throw new Error("fallback path should not execute when rankChunksByIndex is available");
68
+ };
69
+
70
+ const result = await core.searchContext({
71
+ trace_id: "trc-rank",
72
+ tenant_id: "tenant-bench",
73
+ workspace_id: "ws-bench",
74
+ request: {
75
+ project_root_path: "/workspace/bench",
76
+ query: "require tenant auth",
77
+ top_k: 5
78
+ }
79
+ });
80
+
81
+ expect(result.results.length).toBeGreaterThan(0);
82
+ repo.listChunksByIndex = originalList;
83
+ } finally {
84
+ cache.close();
85
+ repo.close();
86
+ }
87
+ });
88
+
89
+ it("meets latency and quality thresholds on benchmark queries", async () => {
90
+ const root = await mkdtemp(join(tmpdir(), "cue-benchmark-"));
91
+ dirs.push(root);
92
+ const sqlitePath = join(root, "bench.sqlite");
93
+
94
+ const repo = new SqliteIndexRepository(sqlitePath);
95
+ await repo.migrate();
96
+ await repo.upsertWorkspace({
97
+ workspace_id: "ws-bench",
98
+ tenant_id: "tenant-bench",
99
+ name: "bench",
100
+ project_root_path: "/workspace/bench"
101
+ });
102
+
103
+ const cache = new SqliteQueryCache(sqlitePath);
104
+ const core = new RetrievalCore(repo, cache);
105
+
106
+ try {
107
+ await core.indexArtifact({
108
+ tenant_id: "tenant-bench",
109
+ workspace_id: "ws-bench",
110
+ index_version: "idx-bench-v1",
111
+ files: [
112
+ {
113
+ path: "src/security/auth.ts",
114
+ language: "typescript",
115
+ content:
116
+ "export function requireTenant(id: string) {\n if (!id) throw new Error('missing tenant');\n return id;\n}\n"
117
+ },
118
+ {
119
+ path: "src/security/rbac.ts",
120
+ language: "typescript",
121
+ content:
122
+ "export function canAccess(role: string, action: string) {\n return role === 'admin' || action === 'read';\n}\n"
123
+ },
124
+ {
125
+ path: "src/retrieval/search.ts",
126
+ language: "typescript",
127
+ content:
128
+ "export function searchContext(query: string) {\n return query.toLowerCase().split(/\\s+/);\n}\n"
129
+ },
130
+ {
131
+ path: "src/retrieval/rerank.ts",
132
+ language: "typescript",
133
+ content:
134
+ "export function rerank(scores: number[]) {\n return [...scores].sort((a, b) => b - a);\n}\n"
135
+ },
136
+ {
137
+ path: "src/indexer/queue.ts",
138
+ language: "typescript",
139
+ content:
140
+ "export async function claimNext() {\n return { job_id: 'job-1', status: 'processing' };\n}\n"
141
+ },
142
+ {
143
+ path: "src/indexer/worker.ts",
144
+ language: "typescript",
145
+ content:
146
+ "export async function processIndexJob() {\n return { status: 'ready', added: 1 };\n}\n"
147
+ },
148
+ {
149
+ path: "src/observability/metrics.ts",
150
+ language: "typescript",
151
+ content:
152
+ "export function emitMetric(name: string, value: number) {\n return `${name}:${value}`;\n}\n"
153
+ },
154
+ {
155
+ path: "src/billing/metering.ts",
156
+ language: "typescript",
157
+ content:
158
+ "export function recordUsage(tenantId: string, units: number) {\n return { tenantId, units };\n}\n"
159
+ }
160
+ ]
161
+ });
162
+
163
+ const suite = [
164
+ { query: "tenant auth check", expected_path: "src/security/auth.ts" },
165
+ { query: "rbac access role action", expected_path: "src/security/rbac.ts" },
166
+ { query: "search context query tokenizer", expected_path: "src/retrieval/search.ts" },
167
+ { query: "rerank score sorting", expected_path: "src/retrieval/rerank.ts" },
168
+ { query: "queue claim next processing", expected_path: "src/indexer/queue.ts" },
169
+ { query: "index worker status ready", expected_path: "src/indexer/worker.ts" },
170
+ { query: "emit metric name value", expected_path: "src/observability/metrics.ts" },
171
+ { query: "billing usage metering", expected_path: "src/billing/metering.ts" }
172
+ ];
173
+
174
+ let hits = 0;
175
+ const latencies: number[] = [];
176
+
177
+ for (const scenario of suite) {
178
+ const result = await core.searchContext({
179
+ trace_id: `trc-bench-${scenario.expected_path}`,
180
+ tenant_id: "tenant-bench",
181
+ workspace_id: "ws-bench",
182
+ request: {
183
+ project_root_path: "/workspace/bench",
184
+ query: scenario.query,
185
+ top_k: 5
186
+ }
187
+ });
188
+
189
+ latencies.push(result.search_metadata.latency_ms);
190
+ if (result.results.slice(0, 5).some((candidate) => candidate.path === scenario.expected_path)) {
191
+ hits += 1;
192
+ }
193
+ }
194
+
195
+ const hitRatio = hits / suite.length;
196
+ const p95 = percentile(latencies, 0.95);
197
+
198
+ expect(hitRatio).toBeGreaterThanOrEqual(QUALITY_THRESHOLD);
199
+ expect(p95).toBeLessThanOrEqual(P95_LATENCY_THRESHOLD_MS);
200
+ } finally {
201
+ cache.close();
202
+ repo.close();
203
+ }
204
+ });
205
+
206
+ it("prefers source paths over archived docs for implementation queries", async () => {
207
+ const root = await mkdtemp(join(tmpdir(), "cue-path-bias-"));
208
+ dirs.push(root);
209
+ const sqlitePath = join(root, "bias.sqlite");
210
+
211
+ const repo = new SqliteIndexRepository(sqlitePath);
212
+ await repo.migrate();
213
+ await repo.upsertWorkspace({
214
+ workspace_id: "ws-bias",
215
+ tenant_id: "tenant-bias",
216
+ name: "bias",
217
+ project_root_path: "/workspace/bias"
218
+ });
219
+
220
+ const cache = new SqliteQueryCache(sqlitePath);
221
+ const core = new RetrievalCore(repo, cache);
222
+
223
+ try {
224
+ await core.indexArtifact({
225
+ tenant_id: "tenant-bias",
226
+ workspace_id: "ws-bias",
227
+ index_version: "idx-bias-v1",
228
+ files: [
229
+ {
230
+ path: "src/security/session.ts",
231
+ language: "typescript",
232
+ content:
233
+ "export function validateSessionToken(token: string) {\n return token.length > 10;\n}\n"
234
+ },
235
+ {
236
+ path: "docs/_archive/security-notes.md",
237
+ language: "markdown",
238
+ content:
239
+ "session token validation implementation notes and historical behavior for archived systems."
240
+ }
241
+ ]
242
+ });
243
+
244
+ const result = await core.searchContext({
245
+ trace_id: "trc-path-bias",
246
+ tenant_id: "tenant-bias",
247
+ workspace_id: "ws-bias",
248
+ request: {
249
+ project_root_path: "/workspace/bias",
250
+ query: "where is session token validation implemented",
251
+ top_k: 5
252
+ }
253
+ });
254
+
255
+ expect(result.results[0]?.path).toBe("src/security/session.ts");
256
+ } finally {
257
+ cache.close();
258
+ repo.close();
259
+ }
260
+ });
261
+
262
+ it("matches slash-delimited query terms against source identifiers", async () => {
263
+ const root = await mkdtemp(join(tmpdir(), "cue-tokenize-"));
264
+ dirs.push(root);
265
+ const sqlitePath = join(root, "tokenize.sqlite");
266
+
267
+ const repo = new SqliteIndexRepository(sqlitePath);
268
+ await repo.migrate();
269
+ await repo.upsertWorkspace({
270
+ workspace_id: "ws-tokenize",
271
+ tenant_id: "tenant-tokenize",
272
+ name: "tokenize",
273
+ project_root_path: "/workspace/tokenize"
274
+ });
275
+
276
+ const cache = new SqliteQueryCache(sqlitePath);
277
+ const core = new RetrievalCore(repo, cache);
278
+
279
+ try {
280
+ await core.indexArtifact({
281
+ tenant_id: "tenant-tokenize",
282
+ workspace_id: "ws-tokenize",
283
+ index_version: "idx-tokenize-v1",
284
+ files: [
285
+ {
286
+ path: "src/security/session.ts",
287
+ language: "typescript",
288
+ content:
289
+ "export function validateSessionToken(token: string) {\n return token.startsWith('tok_');\n}\n"
290
+ }
291
+ ]
292
+ });
293
+
294
+ const result = await core.searchContext({
295
+ trace_id: "trc-tokenize",
296
+ tenant_id: "tenant-tokenize",
297
+ workspace_id: "ws-tokenize",
298
+ request: {
299
+ project_root_path: "/workspace/tokenize",
300
+ query: "where is auth/session/token validation implemented",
301
+ top_k: 5
302
+ }
303
+ });
304
+
305
+ expect(result.results[0]?.path).toBe("src/security/session.ts");
306
+ } finally {
307
+ cache.close();
308
+ repo.close();
309
+ }
310
+ });
311
+
312
+ it("matches singular and plural morphology for path tokens", async () => {
313
+ const root = await mkdtemp(join(tmpdir(), "cue-morphology-"));
314
+ dirs.push(root);
315
+ const sqlitePath = join(root, "morphology.sqlite");
316
+
317
+ const repo = new SqliteIndexRepository(sqlitePath);
318
+ await repo.migrate();
319
+ await repo.upsertWorkspace({
320
+ workspace_id: "ws-morphology",
321
+ tenant_id: "tenant-morphology",
322
+ name: "morphology",
323
+ project_root_path: "/workspace/morphology"
324
+ });
325
+
326
+ const cache = new SqliteQueryCache(sqlitePath);
327
+ const core = new RetrievalCore(repo, cache);
328
+
329
+ try {
330
+ await core.indexArtifact({
331
+ tenant_id: "tenant-morphology",
332
+ workspace_id: "ws-morphology",
333
+ index_version: "idx-morphology-v1",
334
+ files: [
335
+ {
336
+ path: "src/flask/blueprints.py",
337
+ language: "python",
338
+ content:
339
+ "def register_blueprint(app, blueprint):\n app.blueprints[blueprint.name] = blueprint\n return app\n"
340
+ }
341
+ ]
342
+ });
343
+
344
+ const result = await core.searchContext({
345
+ trace_id: "trc-morphology",
346
+ tenant_id: "tenant-morphology",
347
+ workspace_id: "ws-morphology",
348
+ request: {
349
+ project_root_path: "/workspace/morphology",
350
+ query: "where is blueprint registration implemented",
351
+ top_k: 5
352
+ }
353
+ });
354
+
355
+ expect(result.results[0]?.path).toBe("src/flask/blueprints.py");
356
+ } finally {
357
+ cache.close();
358
+ repo.close();
359
+ }
360
+ });
361
+
362
+ it("downranks lockfiles for implementation queries", async () => {
363
+ const root = await mkdtemp(join(tmpdir(), "cue-lockfile-"));
364
+ dirs.push(root);
365
+ const sqlitePath = join(root, "lockfile.sqlite");
366
+
367
+ const repo = new SqliteIndexRepository(sqlitePath);
368
+ await repo.migrate();
369
+ await repo.upsertWorkspace({
370
+ workspace_id: "ws-lockfile",
371
+ tenant_id: "tenant-lockfile",
372
+ name: "lockfile",
373
+ project_root_path: "/workspace/lockfile"
374
+ });
375
+
376
+ const cache = new SqliteQueryCache(sqlitePath);
377
+ const core = new RetrievalCore(repo, cache);
378
+
379
+ try {
380
+ await core.indexArtifact({
381
+ tenant_id: "tenant-lockfile",
382
+ workspace_id: "ws-lockfile",
383
+ index_version: "idx-lockfile-v1",
384
+ files: [
385
+ {
386
+ path: "bun.lock",
387
+ content: "session token validation auth tenant retry queue"
388
+ },
389
+ {
390
+ path: "src/security/auth.ts",
391
+ language: "typescript",
392
+ content:
393
+ "export function validateTenantSessionToken(token: string) {\n return token.length > 16;\n}\n"
394
+ }
395
+ ]
396
+ });
397
+
398
+ const result = await core.searchContext({
399
+ trace_id: "trc-lockfile",
400
+ tenant_id: "tenant-lockfile",
401
+ workspace_id: "ws-lockfile",
402
+ request: {
403
+ project_root_path: "/workspace/lockfile",
404
+ query: "where is tenant session token validation implemented",
405
+ top_k: 5
406
+ }
407
+ });
408
+
409
+ expect(result.results[0]?.path).toBe("src/security/auth.ts");
410
+ } finally {
411
+ cache.close();
412
+ repo.close();
413
+ }
414
+ });
415
+
416
+ it("supports conservative scoring profile without breaking retrieval", async () => {
417
+ const root = await mkdtemp(join(tmpdir(), "cue-profile-"));
418
+ dirs.push(root);
419
+ const sqlitePath = join(root, "profile.sqlite");
420
+
421
+ const repo = new SqliteIndexRepository(sqlitePath);
422
+ await repo.migrate();
423
+ await repo.upsertWorkspace({
424
+ workspace_id: "ws-profile",
425
+ tenant_id: "tenant-profile",
426
+ name: "profile",
427
+ project_root_path: "/workspace/profile"
428
+ });
429
+
430
+ const cache = new SqliteQueryCache(sqlitePath);
431
+ const core = new RetrievalCore(repo, cache, {
432
+ scoringProfile: "conservative",
433
+ scoringProfileId: "conservative:test"
434
+ });
435
+
436
+ try {
437
+ await core.indexArtifact({
438
+ tenant_id: "tenant-profile",
439
+ workspace_id: "ws-profile",
440
+ index_version: "idx-profile-v1",
441
+ files: [
442
+ {
443
+ path: "src/security/auth.ts",
444
+ language: "typescript",
445
+ content:
446
+ "export function requireTenant(id: string) {\n if (!id) throw new Error('missing tenant');\n return id;\n}\n"
447
+ }
448
+ ]
449
+ });
450
+
451
+ const result = await core.searchContext({
452
+ trace_id: "trc-profile",
453
+ tenant_id: "tenant-profile",
454
+ workspace_id: "ws-profile",
455
+ request: {
456
+ project_root_path: "/workspace/profile",
457
+ query: "where is requireTenant implemented",
458
+ top_k: 5
459
+ }
460
+ });
461
+
462
+ expect(result.results.length).toBeGreaterThan(0);
463
+ expect(result.results[0]?.path).toBe("src/security/auth.ts");
464
+ } finally {
465
+ cache.close();
466
+ repo.close();
467
+ }
468
+ });
469
+
470
+ it("downranks test files for implementation queries", async () => {
471
+ const root = await mkdtemp(join(tmpdir(), "cue-test-path-bias-"));
472
+ dirs.push(root);
473
+ const sqlitePath = join(root, "test-path-bias.sqlite");
474
+
475
+ const repo = new SqliteIndexRepository(sqlitePath);
476
+ await repo.migrate();
477
+ await repo.upsertWorkspace({
478
+ workspace_id: "ws-test-path-bias",
479
+ tenant_id: "tenant-test-path-bias",
480
+ name: "test-path-bias",
481
+ project_root_path: "/workspace/test-path-bias"
482
+ });
483
+
484
+ const cache = new SqliteQueryCache(sqlitePath);
485
+ const core = new RetrievalCore(repo, cache);
486
+
487
+ try {
488
+ await core.indexArtifact({
489
+ tenant_id: "tenant-test-path-bias",
490
+ workspace_id: "ws-test-path-bias",
491
+ index_version: "idx-test-path-bias-v1",
492
+ files: [
493
+ {
494
+ path: "lib/request.js",
495
+ language: "javascript",
496
+ content:
497
+ "function parseRequestHeaders(req) {\n return req.headers;\n}\nmodule.exports = { parseRequestHeaders };\n"
498
+ },
499
+ {
500
+ path: "test/request.test.js",
501
+ language: "javascript",
502
+ content:
503
+ "test('request headers parser', () => {\n expect(parseRequestHeaders({ headers: {} })).toEqual({});\n});\n"
504
+ }
505
+ ]
506
+ });
507
+
508
+ const result = await core.searchContext({
509
+ trace_id: "trc-test-path-bias",
510
+ tenant_id: "tenant-test-path-bias",
511
+ workspace_id: "ws-test-path-bias",
512
+ request: {
513
+ project_root_path: "/workspace/test-path-bias",
514
+ query: "where is request headers parser implemented",
515
+ top_k: 5
516
+ }
517
+ });
518
+
519
+ expect(result.results[0]?.path).toBe("lib/request.js");
520
+ } finally {
521
+ cache.close();
522
+ repo.close();
523
+ }
524
+ });
525
+
526
+ it("matches camelCase identifiers with split query terms", async () => {
527
+ const root = await mkdtemp(join(tmpdir(), "cue-camel-case-"));
528
+ dirs.push(root);
529
+ const sqlitePath = join(root, "camel-case.sqlite");
530
+
531
+ const repo = new SqliteIndexRepository(sqlitePath);
532
+ await repo.migrate();
533
+ await repo.upsertWorkspace({
534
+ workspace_id: "ws-camel-case",
535
+ tenant_id: "tenant-camel-case",
536
+ name: "camel-case",
537
+ project_root_path: "/workspace/camel-case"
538
+ });
539
+
540
+ const cache = new SqliteQueryCache(sqlitePath);
541
+ const core = new RetrievalCore(repo, cache);
542
+
543
+ try {
544
+ await core.indexArtifact({
545
+ tenant_id: "tenant-camel-case",
546
+ workspace_id: "ws-camel-case",
547
+ index_version: "idx-camel-case-v1",
548
+ files: [
549
+ {
550
+ path: "lib/core/mergeConfig.js",
551
+ language: "javascript",
552
+ content:
553
+ "function mergeConfig(base, next) {\n return { ...base, ...next };\n}\nmodule.exports = mergeConfig;\n"
554
+ }
555
+ ]
556
+ });
557
+
558
+ const result = await core.searchContext({
559
+ trace_id: "trc-camel-case",
560
+ tenant_id: "tenant-camel-case",
561
+ workspace_id: "ws-camel-case",
562
+ request: {
563
+ project_root_path: "/workspace/camel-case",
564
+ query: "where are merge config options combined",
565
+ top_k: 5
566
+ }
567
+ });
568
+
569
+ expect(result.results[0]?.path).toBe("lib/core/mergeConfig.js");
570
+ } finally {
571
+ cache.close();
572
+ repo.close();
573
+ }
574
+ });
575
+
576
+ it("matches ALL_CAPS env-var literals for config parsing paths", async () => {
577
+ const root = await mkdtemp(join(tmpdir(), "cue-env-var-tokenize-"));
578
+ dirs.push(root);
579
+ const sqlitePath = join(root, "env-var-tokenize.sqlite");
580
+
581
+ const repo = new SqliteIndexRepository(sqlitePath);
582
+ await repo.migrate();
583
+ await repo.upsertWorkspace({
584
+ workspace_id: "ws-env-var",
585
+ tenant_id: "tenant-env-var",
586
+ name: "env-var",
587
+ project_root_path: "/workspace/env-var"
588
+ });
589
+
590
+ const cache = new SqliteQueryCache(sqlitePath);
591
+ const core = new RetrievalCore(repo, cache);
592
+
593
+ try {
594
+ await core.indexArtifact({
595
+ tenant_id: "tenant-env-var",
596
+ workspace_id: "ws-env-var",
597
+ index_version: "idx-env-var-v1",
598
+ files: [
599
+ {
600
+ path: "apps/mcp-api/src/state.ts",
601
+ language: "typescript",
602
+ content:
603
+ "const CUE_MCP_TOKEN_BINDINGS_JSON = process.env.CUE_MCP_TOKEN_BINDINGS_JSON;\nfunction parseMcpTokenBindingsJson(raw) { return raw; }\n"
604
+ },
605
+ {
606
+ path: "apps/mcp-api/src/mcp-tool-guidance.ts",
607
+ language: "typescript",
608
+ content: "guidance tenant_id workspace_id project_root_path token binding json config"
609
+ }
610
+ ]
611
+ });
612
+
613
+ const result = await core.searchContext({
614
+ trace_id: "trc-env-var-tokenize",
615
+ tenant_id: "tenant-env-var",
616
+ workspace_id: "ws-env-var",
617
+ request: {
618
+ project_root_path: "/workspace/env-var",
619
+ query: "CUE_MCP_TOKEN_BINDINGS_JSON parse token bindings tenant_id workspace_id project_root_path",
620
+ top_k: 5
621
+ }
622
+ });
623
+
624
+ expect(result.results[0]?.path).toBe("apps/mcp-api/src/state.ts");
625
+ } finally {
626
+ cache.close();
627
+ repo.close();
628
+ }
629
+ });
630
+
631
+ it("improves enhancer retrieval for framework-specific symbols", async () => {
632
+ const root = await mkdtemp(join(tmpdir(), "cue-enhancer-symbols-"));
633
+ dirs.push(root);
634
+ const sqlitePath = join(root, "enhancer-symbols.sqlite");
635
+
636
+ const repo = new SqliteIndexRepository(sqlitePath);
637
+ await repo.migrate();
638
+ await repo.upsertWorkspace({
639
+ workspace_id: "ws-enhancer-symbols",
640
+ tenant_id: "tenant-enhancer-symbols",
641
+ name: "enhancer-symbols",
642
+ project_root_path: "/workspace/enhancer-symbols"
643
+ });
644
+
645
+ const cache = new SqliteQueryCache(sqlitePath);
646
+ const core = new RetrievalCore(repo, cache);
647
+
648
+ try {
649
+ await core.indexArtifact({
650
+ tenant_id: "tenant-enhancer-symbols",
651
+ workspace_id: "ws-enhancer-symbols",
652
+ index_version: "idx-enhancer-symbols-v1",
653
+ files: [
654
+ {
655
+ path: "src/flask/templating.py",
656
+ language: "python",
657
+ content:
658
+ "def render_template(template_name_or_list, **context):\n return _render(app, template_name_or_list, context)\n\ndef render_template_string(source, **context):\n return _render(app, source, context)\n"
659
+ },
660
+ {
661
+ path: "src/flask/signals.py",
662
+ language: "python",
663
+ content: "before_render_template = signal('before-render-template')\n"
664
+ },
665
+ {
666
+ path: "fastify.js",
667
+ language: "javascript",
668
+ content:
669
+ "function addHook(name, fn) { return hooks.add(name, fn) }\nfunction setErrorHandler(fn) { return buildErrorHandler(fn) }\n"
670
+ },
671
+ {
672
+ path: "lib/hooks.js",
673
+ language: "javascript",
674
+ content: "const supportedHooks = ['onRequest', 'preHandler', 'onError'];\nmodule.exports = { supportedHooks };\n"
675
+ },
676
+ {
677
+ path: "lib/error-handler.js",
678
+ language: "javascript",
679
+ content: "function buildErrorHandler(parent, func) { return function handleError(err) { return func(err) } }\n"
680
+ },
681
+ {
682
+ path: "docs/reference/hooks.md",
683
+ language: "markdown",
684
+ content:
685
+ "addHook setErrorHandler render_template examples. This is docs-only guidance and not implementation code."
686
+ }
687
+ ]
688
+ });
689
+
690
+ const scenarios = [
691
+ {
692
+ prompt: "Refactor render_template flow and keep extension compatibility.",
693
+ expected_any_of: ["src/flask/templating.py"]
694
+ },
695
+ {
696
+ prompt: "Tighten addHook lifecycle ordering while preserving plugin behavior.",
697
+ expected_any_of: ["fastify.js", "lib/hooks.js"]
698
+ },
699
+ {
700
+ prompt: "Refine setErrorHandler behavior while preserving request pipeline semantics.",
701
+ expected_any_of: ["fastify.js", "lib/error-handler.js"]
702
+ }
703
+ ];
704
+
705
+ for (const scenario of scenarios) {
706
+ const result = await core.enhancePrompt({
707
+ trace_id: `trc-enhancer-${scenario.expected_any_of[0]}`,
708
+ tenant_id: "tenant-enhancer-symbols",
709
+ workspace_id: "ws-enhancer-symbols",
710
+ request: {
711
+ prompt: scenario.prompt,
712
+ project_root_path: "/workspace/enhancer-symbols",
713
+ conversation_history: [{ role: "user", content: scenario.prompt }]
714
+ }
715
+ });
716
+
717
+ const topPaths = result.context_refs.slice(0, 3).map((ref) => ref.path);
718
+ expect(topPaths.some((path) => scenario.expected_any_of.includes(path))).toBe(true);
719
+ }
720
+ } finally {
721
+ cache.close();
722
+ repo.close();
723
+ }
724
+ });
725
+
726
+ it("collapses same-directory refs for symbol-heavy enhancer prompts", async () => {
727
+ const root = await mkdtemp(join(tmpdir(), "cue-enhancer-collapse-"));
728
+ dirs.push(root);
729
+ const sqlitePath = join(root, "enhancer-collapse.sqlite");
730
+
731
+ const repo = new SqliteIndexRepository(sqlitePath);
732
+ await repo.migrate();
733
+ await repo.upsertWorkspace({
734
+ workspace_id: "ws-enhancer-collapse",
735
+ tenant_id: "tenant-enhancer-collapse",
736
+ name: "enhancer-collapse",
737
+ project_root_path: "/workspace/enhancer-collapse"
738
+ });
739
+
740
+ const cache = new SqliteQueryCache(sqlitePath);
741
+ const core = new RetrievalCore(repo, cache);
742
+
743
+ try {
744
+ await core.indexArtifact({
745
+ tenant_id: "tenant-enhancer-collapse",
746
+ workspace_id: "ws-enhancer-collapse",
747
+ index_version: "idx-enhancer-collapse-v1",
748
+ files: [
749
+ {
750
+ path: "lib/hooks.js",
751
+ language: "javascript",
752
+ content:
753
+ "function addHook(name, fn) { return hooks.add(name, fn) }\nfunction onRequest(fn) { return fn }\nmodule.exports = { addHook, onRequest };\n"
754
+ },
755
+ {
756
+ path: "lib/error-handler.js",
757
+ language: "javascript",
758
+ content:
759
+ "function setErrorHandler(fn) { return buildErrorHandler(fn) }\nmodule.exports = { setErrorHandler };\n"
760
+ },
761
+ {
762
+ path: "lib/route.js",
763
+ language: "javascript",
764
+ content:
765
+ "function preHandler(fn) { return fn }\nfunction onError(fn) { return fn }\nmodule.exports = { preHandler, onError };\n"
766
+ },
767
+ {
768
+ path: "src/runtime/lifecycle.ts",
769
+ language: "typescript",
770
+ content:
771
+ "export function wireLifecycleHooks() { return ['addHook', 'onRequest', 'preHandler', 'setErrorHandler']; }\n"
772
+ }
773
+ ]
774
+ });
775
+
776
+ const result = await core.enhancePrompt({
777
+ trace_id: "trc-enhancer-collapse",
778
+ tenant_id: "tenant-enhancer-collapse",
779
+ workspace_id: "ws-enhancer-collapse",
780
+ request: {
781
+ prompt: "Tighten `setErrorHandler` `addHook` `onRequest` `preHandler` behavior.",
782
+ project_root_path: "/workspace/enhancer-collapse",
783
+ conversation_history: [{ role: "user", content: "Use symbol-focused implementation context only." }]
784
+ }
785
+ });
786
+
787
+ expect(result.context_refs.length).toBeLessThanOrEqual(3);
788
+ const libRefs = result.context_refs.filter((ref) => ref.path.startsWith("lib/"));
789
+ expect(libRefs.length).toBeLessThanOrEqual(1);
790
+ } finally {
791
+ cache.close();
792
+ repo.close();
793
+ }
794
+ });
795
+
796
+ it("rejects invalid scoring config merges", () => {
797
+ const baseline = resolveRetrievalScoringProfile("baseline");
798
+ expect(() =>
799
+ mergeRetrievalScoringConfig(baseline.config, {
800
+ candidate_weights: {
801
+ lexical_weight: 0.8,
802
+ vector_weight: 0.8
803
+ }
804
+ })
805
+ ).toThrow(/lexical_weight \+ vector_weight must equal 1/);
806
+
807
+ expect(() =>
808
+ mergeRetrievalScoringConfig(baseline.config, {
809
+ rerank: {
810
+ merge_gap_lines: -1
811
+ }
812
+ })
813
+ ).toThrow(/merge_gap_lines must be an integer >= 0/);
814
+ });
815
+ });