116 lines
4.4 KiB
TypeScript
116 lines
4.4 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
EXECUTOR_FIT,
|
|
DEFAULT_TASK_FIT,
|
|
resolveTaskFit,
|
|
buildTaskFit,
|
|
classifyTask,
|
|
} from '../src/executor-fit.js';
|
|
import type { TaskCategory } from '../src/executor-router.js';
|
|
|
|
describe('EXECUTOR_FIT static table', () => {
|
|
it('carries the specified persona fits', () => {
|
|
expect(EXECUTOR_FIT['persona:coder']).toEqual({ coding: 0.85 });
|
|
expect(EXECUTOR_FIT['persona:writer']).toEqual({ writing: 0.9 });
|
|
expect(EXECUTOR_FIT['persona:researcher']).toEqual({ research: 0.9 });
|
|
expect(EXECUTOR_FIT['persona:analyst']).toEqual({ analysis: 0.9 });
|
|
});
|
|
|
|
it('gives general-purpose a uniform 0.6 across all categories', () => {
|
|
expect(EXECUTOR_FIT['persona:general-purpose']).toEqual({
|
|
coding: 0.6,
|
|
writing: 0.6,
|
|
research: 0.6,
|
|
analysis: 0.6,
|
|
ops: 0.6,
|
|
general: 0.6,
|
|
});
|
|
});
|
|
|
|
it('carries the specified external CLI fits', () => {
|
|
expect(EXECUTOR_FIT['external:claude-code']).toEqual({ coding: 0.95, analysis: 0.6 });
|
|
expect(EXECUTOR_FIT['external:codex']).toEqual({ coding: 0.9 });
|
|
expect(EXECUTOR_FIT['external:hermes']).toEqual({ coding: 0.6, research: 0.5 });
|
|
expect(EXECUTOR_FIT['external:openclaw']).toEqual({ coding: 0.7, ops: 0.6 });
|
|
});
|
|
});
|
|
|
|
describe('resolveTaskFit', () => {
|
|
it('returns the explicit fit when present', () => {
|
|
expect(resolveTaskFit('persona:coder', 'coding')).toBe(0.85);
|
|
expect(resolveTaskFit('external:claude-code', 'analysis')).toBe(0.6);
|
|
});
|
|
|
|
it('falls back to 0.3 for an unlisted category on a known executor', () => {
|
|
expect(resolveTaskFit('persona:coder', 'writing')).toBe(DEFAULT_TASK_FIT);
|
|
expect(resolveTaskFit('external:codex', 'ops')).toBe(0.3);
|
|
});
|
|
|
|
it('falls back to 0.3 for a completely unknown executor', () => {
|
|
expect(resolveTaskFit('external:mystery', 'coding')).toBe(0.3);
|
|
});
|
|
});
|
|
|
|
describe('buildTaskFit', () => {
|
|
it('default-fills every category for a partial entry', () => {
|
|
expect(buildTaskFit('persona:coder')).toEqual({
|
|
coding: 0.85,
|
|
writing: 0.3,
|
|
research: 0.3,
|
|
analysis: 0.3,
|
|
ops: 0.3,
|
|
general: 0.3,
|
|
});
|
|
});
|
|
|
|
it('returns all-0.3 for an unknown executor', () => {
|
|
const fit = buildTaskFit('external:unknown');
|
|
expect(Object.values(fit).every((v) => v === 0.3)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('classifyTask — category matrix', () => {
|
|
const cases: Array<{ prompt: string; category: TaskCategory }> = [
|
|
{ prompt: 'Please refactor this function and fix the failing test', category: 'coding' },
|
|
{ prompt: 'Update the file app.ts to add a class', category: 'coding' },
|
|
{ prompt: '```js\nconsole.log(1)\n```', category: 'coding' },
|
|
{ prompt: 'Draft a blog post and write a marketing email', category: 'writing' },
|
|
{ prompt: 'Research and compare sources on solar adoption, cite references', category: 'research' },
|
|
{ prompt: 'Analyze the sales report and summarize the key metrics and trends', category: 'analysis' },
|
|
{ prompt: 'Deploy the service and configure the docker pipeline', category: 'ops' },
|
|
{ prompt: 'Tell me a story about a friendly dragon', category: 'general' },
|
|
];
|
|
|
|
for (const { prompt, category } of cases) {
|
|
it(`classifies "${prompt.slice(0, 30)}..." as ${category}`, () => {
|
|
expect(classifyTask(prompt).category).toBe(category);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe('classifyTask — confidence', () => {
|
|
it('returns 0.3 confidence for an unmatched (general) prompt', () => {
|
|
const c = classifyTask('hello there, how are you today');
|
|
expect(c).toEqual({ category: 'general', confidence: 0.3 });
|
|
});
|
|
|
|
it('raises confidence with more matched signals', () => {
|
|
const one = classifyTask('please write this'); // 1 writing signal
|
|
const strong = classifyTask('refactor the function, fix the bug in app.ts'); // 3 coding signals
|
|
expect(one.confidence).toBeCloseTo(0.65, 10);
|
|
expect(strong.confidence).toBeGreaterThan(one.confidence);
|
|
expect(strong.confidence).toBeLessThanOrEqual(0.95);
|
|
});
|
|
|
|
it('never exceeds the 0.95 confidence cap', () => {
|
|
const c = classifyTask('```\nrefactor implement fix debug build test class function\n``` app.ts app.py');
|
|
expect(c.confidence).toBeLessThanOrEqual(0.95);
|
|
});
|
|
|
|
it('breaks a cross-category tie by declaration order (coding first)', () => {
|
|
// one coding signal ("implement") and one writing signal ("write") → coding wins
|
|
const c = classifyTask('write and implement');
|
|
expect(c.category).toBe('coding');
|
|
});
|
|
});
|