explore-pinned-allocation.test.ts 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /**
  2. * Pinned files in `allocateExploreBudget` (see query-paths.ts): a file the
  3. * query named by PATH must survive every allocation guard. Its score is
  4. * whatever the path-stripped query happened to match — for a pure-path query,
  5. * nearly nothing — so without the pinned floor the proportional split would
  6. * fund the one file the agent explicitly asked for worst of all, and the
  7. * cliff would zero it outright.
  8. */
  9. import { describe, it, expect } from 'vitest';
  10. import { allocateExploreBudget, getExploreOutputBudget, EXPLORE_ALLOCATION } from '../src/mcp/tools';
  11. import type { ExploreAllocationCandidate } from '../src/mcp/tools';
  12. const cand = (
  13. path: string,
  14. score: number,
  15. extra: Partial<ExploreAllocationCandidate> = {},
  16. ): ExploreAllocationCandidate => ({ path, score, worth: 1, spine: false, ...extra });
  17. const budget = getExploreOutputBudget(1000);
  18. describe('allocateExploreBudget — pinned files', () => {
  19. it('never cliffs a pinned file, however low it scores', () => {
  20. const { allowances, cliffed } = allocateExploreBudget(
  21. [
  22. cand('pinned.svelte', 0.1, { pinned: true }),
  23. cand('hub.ts', 200),
  24. cand('noise.ts', 0.1),
  25. ],
  26. budget,
  27. 8,
  28. );
  29. expect(cliffed).toContain('noise.ts');
  30. expect(cliffed).not.toContain('pinned.svelte');
  31. expect(allowances.has('pinned.svelte')).toBe(true);
  32. });
  33. it('funds a pinned file at least as well as the strongest candidate', () => {
  34. const { allowances } = allocateExploreBudget(
  35. [
  36. cand('pinned.svelte', 0.5, { pinned: true }),
  37. cand('hub.ts', 300),
  38. cand('helper.ts', 40),
  39. ],
  40. budget,
  41. 8,
  42. );
  43. expect(allowances.get('pinned.svelte')!).toBeGreaterThanOrEqual(allowances.get('hub.ts')!);
  44. expect(allowances.get('pinned.svelte')!).toBeGreaterThan(allowances.get('helper.ts')!);
  45. });
  46. it('keeps pinned files through the affordability trim', () => {
  47. // Smallest tier: affordable = floor(13000 / (MIN_CHARS + FILE_OVERHEAD)) = 14
  48. // slots. 18 equal-weight candidates admitted → the trim must cut 4. The
  49. // pinned file sits last with a TIED weight (the pinned floor lifts it to
  50. // the top weight), so the stable by-weight sort would slice it off — only
  51. // the explicit spine/pinned keep saves it.
  52. const tiny = getExploreOutputBudget(10);
  53. const fleet = Array.from({ length: 17 }, (_, i) => cand(`f${i}.ts`, 100));
  54. fleet.push(cand('pinned.svelte', 0.1, { pinned: true }));
  55. const { allowances, cliffed } = allocateExploreBudget(fleet, tiny, 18);
  56. expect(allowances.has('pinned.svelte')).toBe(true);
  57. expect(cliffed).not.toContain('pinned.svelte');
  58. expect(allowances.size).toBeLessThan(18);
  59. });
  60. it('an all-pinned zero-score call still allocates (pure-path query)', () => {
  61. const { allowances, pool } = allocateExploreBudget(
  62. [cand('a.svelte', 0, { pinned: true }), cand('b.svelte', 0, { pinned: true })],
  63. budget,
  64. 8,
  65. );
  66. expect(pool).toBeGreaterThan(0);
  67. expect(allowances.get('a.svelte')!).toBeGreaterThanOrEqual(EXPLORE_ALLOCATION.MIN_CHARS);
  68. expect(allowances.get('b.svelte')!).toBeGreaterThanOrEqual(EXPLORE_ALLOCATION.MIN_CHARS);
  69. });
  70. it('unpinned behavior is unchanged when no candidate is pinned', () => {
  71. const before = allocateExploreBudget(
  72. [cand('a.ts', 40), cand('b.ts', 10)], budget, 8,
  73. );
  74. expect(before.allowances.get('a.ts')!).toBeGreaterThan(before.allowances.get('b.ts')!);
  75. });
  76. });