@lythos/skill-arena 0.15.7 → 0.16.1

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.
@@ -1,10 +1,10 @@
1
- import { describe, test, expect } from 'bun:test'
1
+ import { describe, it, expect } from 'bun:test'
2
2
  import { computePareto, buildComparativePrompt, toScoreMatrix, normalizeComparativeOutput } from './comparative-judge'
3
3
  import { ArenaManifest, CriterionDef, ComparativeReport } from '@lythos/test-utils/schema'
4
4
  import type { ArenaManifest as ArenaManifestType } from '@lythos/test-utils/schema'
5
5
 
6
6
  describe('computePareto', () => {
7
- test('single participant is always non-dominated', () => {
7
+ it('single participant is always non-dominated', () => {
8
8
  const result = computePareto([
9
9
  { participant_id: 'run-01', scores: { a: 5, b: 3 } },
10
10
  ])
@@ -13,7 +13,7 @@ describe('computePareto', () => {
13
13
  expect(result[0].dominated_by).toEqual([])
14
14
  })
15
15
 
16
- test('clear dominance: run-01 dominates run-02 on all criteria', () => {
16
+ it('clear dominance: run-01 dominates run-02 on all criteria', () => {
17
17
  const result = computePareto([
18
18
  { participant_id: 'run-01', scores: { coverage: 5, relevance: 5 } },
19
19
  { participant_id: 'run-02', scores: { coverage: 3, relevance: 2 } },
@@ -23,7 +23,7 @@ describe('computePareto', () => {
23
23
  expect(result[1].dominated_by).toEqual(['run-01'])
24
24
  })
25
25
 
26
- test('equal scores: no one dominates', () => {
26
+ it('equal scores: no one dominates', () => {
27
27
  const result = computePareto([
28
28
  { participant_id: 'run-01', scores: { a: 4, b: 4 } },
29
29
  { participant_id: 'run-02', scores: { a: 4, b: 4 } },
@@ -32,7 +32,7 @@ describe('computePareto', () => {
32
32
  expect(result[1].dominated).toBe(false)
33
33
  })
34
34
 
35
- test('cross dominance: each wins on different criteria', () => {
35
+ it('cross dominance: each wins on different criteria', () => {
36
36
  const result = computePareto([
37
37
  { participant_id: 'run-01', scores: { speed: 5, accuracy: 2 } },
38
38
  { participant_id: 'run-02', scores: { speed: 2, accuracy: 5 } },
@@ -42,7 +42,7 @@ describe('computePareto', () => {
42
42
  expect(result[1].dominated).toBe(false)
43
43
  })
44
44
 
45
- test('multi-participant: transitive dominance chain', () => {
45
+ it('multi-participant: transitive dominance chain', () => {
46
46
  const result = computePareto([
47
47
  { participant_id: 'best', scores: { a: 5, b: 5, c: 5 } },
48
48
  { participant_id: 'mid', scores: { a: 4, b: 4, c: 4 } },
@@ -56,7 +56,7 @@ describe('computePareto', () => {
56
56
  expect(result[2].dominated_by.sort()).toEqual(['best', 'mid'].sort())
57
57
  })
58
58
 
59
- test('Pareto frontier from playground BDD-research: run-01 dominates run-02', () => {
59
+ it('Pareto frontier from playground BDD-research: run-01 dominates run-02', () => {
60
60
  // From playground/arena-bdd-research/report.md:
61
61
  // Run-01: coverage=5, relevance=5, actionability=5, depth=5
62
62
  // Run-02: coverage=3, relevance=2, actionability=2, depth=1
@@ -69,7 +69,7 @@ describe('computePareto', () => {
69
69
  expect(result[1].dominated_by).toEqual(['run-01'])
70
70
  })
71
71
 
72
- test('empty scores object', () => {
72
+ it('empty scores object', () => {
73
73
  const result = computePareto([
74
74
  { participant_id: 'a', scores: {} },
75
75
  { participant_id: 'b', scores: {} },
@@ -79,7 +79,7 @@ describe('computePareto', () => {
79
79
  expect(result[1].dominated).toBe(false)
80
80
  })
81
81
 
82
- test('partial criteria overlap', () => {
82
+ it('partial criteria overlap', () => {
83
83
  const result = computePareto([
84
84
  { participant_id: 'run-01', scores: { a: 5, b: 3 } },
85
85
  { participant_id: 'run-02', scores: { a: 3, c: 5 } },
@@ -103,7 +103,7 @@ const manifestFixture: ArenaManifest = {
103
103
  }
104
104
 
105
105
  describe('buildComparativePrompt', () => {
106
- test('includes task description', () => {
106
+ it('includes task description', () => {
107
107
  const prompt = buildComparativePrompt({
108
108
  manifest: manifestFixture,
109
109
  verdicts: [],
@@ -111,7 +111,7 @@ describe('buildComparativePrompt', () => {
111
111
  expect(prompt).toContain('Write a function that adds two numbers')
112
112
  })
113
113
 
114
- test('includes all participants', () => {
114
+ it('includes all participants', () => {
115
115
  const prompt = buildComparativePrompt({
116
116
  manifest: manifestFixture,
117
117
  verdicts: [],
@@ -122,7 +122,7 @@ describe('buildComparativePrompt', () => {
122
122
  expect(prompt).toContain('Full test discipline')
123
123
  })
124
124
 
125
- test('includes criteria list', () => {
125
+ it('includes criteria list', () => {
126
126
  const prompt = buildComparativePrompt({
127
127
  manifest: manifestFixture,
128
128
  verdicts: [],
@@ -131,7 +131,7 @@ describe('buildComparativePrompt', () => {
131
131
  expect(prompt).toContain('efficiency')
132
132
  })
133
133
 
134
- test('includes Zod schema in output spec', () => {
134
+ it('includes Zod schema in output spec', () => {
135
135
  const prompt = buildComparativePrompt({
136
136
  manifest: manifestFixture,
137
137
  verdicts: [],
@@ -145,7 +145,7 @@ describe('buildComparativePrompt', () => {
145
145
  // ── toScoreMatrix (pure Zod validation wrapper) ──────────────────────
146
146
 
147
147
  describe('toScoreMatrix', () => {
148
- test('passes through valid score cells', () => {
148
+ it('passes through valid score cells', () => {
149
149
  const result = toScoreMatrix(manifestFixture, [
150
150
  { participant_id: 'bare', criterion: 'correctness', weight: 0.5, score: 4, rationale: 'works' },
151
151
  { participant_id: 'bare', criterion: 'efficiency', weight: 0.5, score: 3, rationale: 'ok' },
@@ -166,7 +166,7 @@ const sampleScoreMatrix = [
166
166
  ]
167
167
 
168
168
  describe('normalizeComparativeOutput', () => {
169
- test('passes through already-correct format', () => {
169
+ it('passes through already-correct format', () => {
170
170
  const input = {
171
171
  score_matrix: sampleScoreMatrix,
172
172
  key_findings: ['TDD produced cleaner code'],
@@ -177,7 +177,7 @@ describe('normalizeComparativeOutput', () => {
177
177
  expect((result.score_matrix as any[])[0].participant_id).toBe('bare')
178
178
  })
179
179
 
180
- test('maps participantId to participant_id', () => {
180
+ it('maps participantId to participant_id', () => {
181
181
  const input: Record<string, unknown> = {
182
182
  score_matrix: [
183
183
  { participantId: 'bare', criterion: 'accuracy', weight: 0.5, score: 4, rationale: 'good' },
@@ -189,7 +189,7 @@ describe('normalizeComparativeOutput', () => {
189
189
  expect((result.score_matrix as any[])[0].participant_id).toBe('bare')
190
190
  })
191
191
 
192
- test('maps side to participant_id', () => {
192
+ it('maps side to participant_id', () => {
193
193
  const input: Record<string, unknown> = {
194
194
  score_matrix: [
195
195
  { side: 'tdd', criterion: 'quality', weight: 0.5, score: 5, rationale: 'excellent' },
@@ -201,7 +201,7 @@ describe('normalizeComparativeOutput', () => {
201
201
  expect((result.score_matrix as any[])[0].participant_id).toBe('tdd')
202
202
  })
203
203
 
204
- test('coerces string score to number', () => {
204
+ it('coerces string score to number', () => {
205
205
  const input: Record<string, unknown> = {
206
206
  score_matrix: [
207
207
  { participant_id: 'bare', criterion: 'a', weight: 0.5, score: '4', rationale: 'ok' },
@@ -213,7 +213,7 @@ describe('normalizeComparativeOutput', () => {
213
213
  expect((result.score_matrix as any[])[0].score).toBe(4)
214
214
  })
215
215
 
216
- test('normalizes weight >1 as percentage', () => {
216
+ it('normalizes weight >1 as percentage', () => {
217
217
  const input: Record<string, unknown> = {
218
218
  score_matrix: [
219
219
  { participant_id: 'bare', criterion: 'a', weight: 50, score: 4, rationale: 'ok' },
@@ -225,7 +225,7 @@ describe('normalizeComparativeOutput', () => {
225
225
  expect((result.score_matrix as any[])[0].weight).toBe(0.5)
226
226
  })
227
227
 
228
- test('defaults weight to 0.25 when undefined', () => {
228
+ it('defaults weight to 0.25 when undefined', () => {
229
229
  const input: Record<string, unknown> = {
230
230
  score_matrix: [
231
231
  { participant_id: 'bare', criterion: 'a', score: 4, rationale: 'ok' },
@@ -237,7 +237,7 @@ describe('normalizeComparativeOutput', () => {
237
237
  expect((result.score_matrix as any[])[0].weight).toBe(0.25)
238
238
  })
239
239
 
240
- test('maps reason to rationale', () => {
240
+ it('maps reason to rationale', () => {
241
241
  const input: Record<string, unknown> = {
242
242
  score_matrix: [
243
243
  { participant_id: 'bare', criterion: 'a', weight: 0.5, score: 4, reason: 'looks fine' },
@@ -249,7 +249,7 @@ describe('normalizeComparativeOutput', () => {
249
249
  expect((result.score_matrix as any[])[0].rationale).toBe('looks fine')
250
250
  })
251
251
 
252
- test('maps explanation to rationale', () => {
252
+ it('maps explanation to rationale', () => {
253
253
  const input: Record<string, unknown> = {
254
254
  score_matrix: [
255
255
  { participant_id: 'bare', criterion: 'a', weight: 0.5, score: 4, explanation: 'works' },
@@ -261,7 +261,7 @@ describe('normalizeComparativeOutput', () => {
261
261
  expect((result.score_matrix as any[])[0].rationale).toBe('works')
262
262
  })
263
263
 
264
- test('normalizes recommendations with role fallback', () => {
264
+ it('normalizes recommendations with role fallback', () => {
265
265
  const input: Record<string, unknown> = {
266
266
  score_matrix: [],
267
267
  key_findings: [],
@@ -275,7 +275,7 @@ describe('normalizeComparativeOutput', () => {
275
275
  expect(recs[0].recommendation).toBe('Use TDD')
276
276
  })
277
277
 
278
- test('normalizes recommendations with advice fallback', () => {
278
+ it('normalizes recommendations with advice fallback', () => {
279
279
  const input: Record<string, unknown> = {
280
280
  score_matrix: [],
281
281
  key_findings: [],
@@ -287,7 +287,7 @@ describe('normalizeComparativeOutput', () => {
287
287
  expect((result.recommendations as any[])[0].recommendation).toBe('Consider refactoring')
288
288
  })
289
289
 
290
- test('handles empty key_findings', () => {
290
+ it('handles empty key_findings', () => {
291
291
  const input: Record<string, unknown> = {
292
292
  score_matrix: [],
293
293
  }
@@ -295,7 +295,7 @@ describe('normalizeComparativeOutput', () => {
295
295
  expect(result.key_findings).toEqual([])
296
296
  })
297
297
 
298
- test('converts pivot-table format: { participant: { criterion: score } }', () => {
298
+ it('converts pivot-table format: { participant: { criterion: score } }', () => {
299
299
  const input: Record<string, unknown> = {
300
300
  bare: { correctness: 4, correctness_rationale: 'works', efficiency: 3, efficiency_rationale: 'ok' },
301
301
  tdd: { correctness: 5, correctness_rationale: 'tests', efficiency: 4, efficiency_rationale: 'clean' },
@@ -310,7 +310,7 @@ describe('normalizeComparativeOutput', () => {
310
310
  expect(bareCorrectness.weight).toBe(0.25)
311
311
  })
312
312
 
313
- test('clamps score to 1-5 range', () => {
313
+ it('clamps score to 1-5 range', () => {
314
314
  const input: Record<string, unknown> = {
315
315
  score_matrix: [
316
316
  { participant_id: 'bare', criterion: 'a', weight: 0.5, score: 0, rationale: 'terrible' },
@@ -367,7 +367,7 @@ const manifestWithRubrics: ArenaManifestType = {
367
367
  }
368
368
 
369
369
  describe('buildComparativePrompt with structured criteria', () => {
370
- test('injects rubric anchors into prompt', () => {
370
+ it('injects rubric anchors into prompt', () => {
371
371
  const prompt = buildComparativePrompt({ manifest: manifestWithRubrics, verdicts: [] })
372
372
  expect(prompt).toContain('信息准确性')
373
373
  expect(prompt).toContain('Evaluator: ISTJ测试员')
@@ -377,7 +377,7 @@ describe('buildComparativePrompt with structured criteria', () => {
377
377
  expect(prompt).toContain('Evaluator: INTJ架构师')
378
378
  })
379
379
 
380
- test('falls back to bare format for string criteria', () => {
380
+ it('falls back to bare format for string criteria', () => {
381
381
  const manifest: ArenaManifestType = {
382
382
  id: 'test', created_at: '2026-01-01T00:00:00Z', task: 'test', mode: 'decks',
383
383
  participants: [{ id: 'a', name: 'A', deck: 'd1' }, { id: 'b', name: 'B', deck: 'd2' }],
@@ -393,7 +393,7 @@ describe('buildComparativePrompt with structured criteria', () => {
393
393
  // Simulate a realistic LLM judge output — the kind of JSON an actual Claude
394
394
  // comparative judge call would produce. Verify our normalization handles it.
395
395
  describe('full pipeline: mock LLM output → schema validation', () => {
396
- test('clean score_matrix passes through ComparativeReport.parse', () => {
396
+ it('clean score_matrix passes through ComparativeReport.parse', () => {
397
397
  const cleanOutput = {
398
398
  score_matrix: [
399
399
  { participant_id: 'bare', criterion: 'accuracy', weight: 0.4, score: 3, rationale: 'Correct on Bun version but missed pnpm migration detail' },
@@ -419,7 +419,7 @@ describe('full pipeline: mock LLM output → schema validation', () => {
419
419
  expect(deepAccuracy!.score).toBe(5)
420
420
  })
421
421
 
422
- test('messy LLM output with field name variants gets normalized', () => {
422
+ it('messy LLM output with field name variants gets normalized', () => {
423
423
  // Simulates a messy Claude output — participantId instead of participant_id,
424
424
  // reason instead of rationale, string score
425
425
  const messyLLMOutput = {
@@ -1,4 +1,4 @@
1
- import { describe, test, expect } from 'bun:test'
1
+ import { describe, it, expect } from 'bun:test'
2
2
  import { resolvePlayer, resolveSides, groupBySide, totalRuns } from './player'
3
3
  import { parseArenaToml } from './arena-toml'
4
4
 
@@ -20,29 +20,29 @@ deck = "./decks/rich.toml"
20
20
  `)
21
21
 
22
22
  describe('resolvePlayer', () => {
23
- test('maps claude-code → claude', () => {
23
+ it('maps claude-code → claude', () => {
24
24
  expect(resolvePlayer('claude-code')).toBe('claude-sdk')
25
25
  })
26
26
 
27
- test('maps Claude → claude (case insensitive)', () => {
27
+ it('maps Claude → claude (case insensitive)', () => {
28
28
  expect(resolvePlayer('Claude')).toBe('claude-sdk')
29
29
  })
30
30
 
31
- test('maps kimi → kimi', () => {
31
+ it('maps kimi → kimi', () => {
32
32
  expect(resolvePlayer('kimi')).toBe('kimi')
33
33
  })
34
34
 
35
- test('passes through unknown player names', () => {
35
+ it('passes through unknown player names', () => {
36
36
  expect(resolvePlayer('expert-architect')).toBe('expert-architect')
37
37
  })
38
38
 
39
- test('trims whitespace', () => {
39
+ it('trims whitespace', () => {
40
40
  expect(resolvePlayer(' claude-code ')).toBe('claude-sdk')
41
41
  })
42
42
  })
43
43
 
44
44
  describe('resolveSides', () => {
45
- test('resolves all sides in arena.toml', () => {
45
+ it('resolves all sides in arena.toml', () => {
46
46
  const sides = resolveSides(toml)
47
47
  expect(sides).toHaveLength(2)
48
48
  expect(sides[0].platform).toBe('claude-sdk')
@@ -50,7 +50,7 @@ describe('resolveSides', () => {
50
50
  expect(sides[0].playerName).toBe('claude-code')
51
51
  })
52
52
 
53
- test('preserves side config', () => {
53
+ it('preserves side config', () => {
54
54
  const sides = resolveSides(toml)
55
55
  expect(sides[0].side.name).toBe('minimal')
56
56
  expect(sides[0].side.deck).toBe('./decks/minimal.toml')
@@ -58,7 +58,7 @@ describe('resolveSides', () => {
58
58
  })
59
59
 
60
60
  describe('groupBySide', () => {
61
- test('groups by side name with run count', () => {
61
+ it('groups by side name with run count', () => {
62
62
  const groups = groupBySide(toml)
63
63
  expect(groups).toHaveLength(2)
64
64
  expect(groups[0].runs).toBe(3) // runs_per_side
@@ -66,7 +66,7 @@ describe('groupBySide', () => {
66
66
  expect(groups[0].platform).toBe('claude-sdk')
67
67
  })
68
68
 
69
- test('control flag preserved', () => {
69
+ it('control flag preserved', () => {
70
70
  const controlToml = parseArenaToml(`
71
71
  [arena]
72
72
  task = "x"
@@ -89,7 +89,7 @@ control = true
89
89
  })
90
90
 
91
91
  describe('totalRuns', () => {
92
- test('calculates sides × runs_per_side', () => {
92
+ it('calculates sides × runs_per_side', () => {
93
93
  expect(totalRuns(toml)).toBe(6) // 2 sides × 3 runs
94
94
  })
95
95
  })