pi-reason-harness 1.0.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.
@@ -0,0 +1,2095 @@
1
+ /**
2
+ * pi-reason-harness — Server unit tests
3
+ *
4
+ * Tests the core algorithms: voting, soft scoring, feedback building,
5
+ * strategy adaptation, budget tracking, and model resolution.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
9
+ import type { RoutedSubProblem, IterationAdaptation, ArcChallenge } from './server.js';
10
+ import { applyIterationAdaptation } from './server.js';
11
+
12
+ // We test the pure functions by extracting them for testability.
13
+ // In production these live in server.ts; for tests we inline the critical ones.
14
+
15
+ function ensure2D(arr: unknown): number[][] | null {
16
+ if (!Array.isArray(arr)) return null;
17
+ if (arr.length === 0) return [[]];
18
+ if (Array.isArray(arr[0])) return arr as number[][];
19
+ return [arr as unknown[] as number[]];
20
+ }
21
+
22
+ function gridShape(grid: number[][]): [number, number] {
23
+ return [grid.length, grid.length > 0 ? grid[0].length : 0];
24
+ }
25
+
26
+ function arrayDiff(pred: number[][], truth: number[][]): string {
27
+ const rows = truth.length;
28
+ const cols = truth.length > 0 ? truth[0].length : 0;
29
+ const lines: string[] = [];
30
+ for (let i = 0; i < rows; i++) {
31
+ const row: string[] = [];
32
+ const pRow = i < pred.length ? pred[i] : [];
33
+ const tRow = truth[i];
34
+ for (let j = 0; j < cols; j++) {
35
+ const pVal = j < pRow.length ? pRow[j] : '?';
36
+ const tVal = tRow[j];
37
+ if (pVal === tVal) {
38
+ row.push(String(tVal));
39
+ } else {
40
+ row.push(`${pVal}/${tVal}`);
41
+ }
42
+ }
43
+ lines.push(row.join(' '));
44
+ }
45
+ return lines.join('\n');
46
+ }
47
+
48
+ function gridToDiagram(grid: number[][]): string {
49
+ return grid.map(row => row.join(' ')).join('\n');
50
+ }
51
+
52
+ function computeSoftScore(actual: string, expected: unknown): number {
53
+ try {
54
+ const actualArr = JSON.parse(actual);
55
+ const expectedArr = Array.isArray(expected) ? expected : JSON.parse(JSON.stringify(expected));
56
+
57
+ if (!Array.isArray(actualArr) || !Array.isArray(expectedArr)) return 0;
58
+
59
+ const pred2D = ensure2D(actualArr);
60
+ const truth2D = ensure2D(expectedArr);
61
+
62
+ if (!pred2D || !truth2D) return 0;
63
+
64
+ const [predRows, predCols] = gridShape(pred2D);
65
+ const [truthRows, truthCols] = gridShape(truth2D);
66
+
67
+ if (predRows !== truthRows || predCols !== truthCols) return 0;
68
+ if (truthRows === 0 || truthCols === 0) return 1;
69
+
70
+ let matches = 0;
71
+ const total = truthRows * truthCols;
72
+ for (let i = 0; i < truthRows; i++) {
73
+ for (let j = 0; j < truthCols; j++) {
74
+ if (pred2D[i][j] === truth2D[i][j]) matches++;
75
+ }
76
+ }
77
+
78
+ return total > 0 ? matches / total : 0;
79
+ } catch {
80
+ return 0;
81
+ }
82
+ }
83
+
84
+ function compareOutputs(actual: string, expected: unknown): boolean {
85
+ try {
86
+ const actualParsed = JSON.parse(actual);
87
+ const expectedParsed = Array.isArray(expected) ? expected : JSON.parse(JSON.stringify(expected));
88
+
89
+ const pred2D = ensure2D(actualParsed);
90
+ const truth2D = ensure2D(expectedParsed);
91
+
92
+ if (pred2D && truth2D) {
93
+ const [pr, pc] = gridShape(pred2D);
94
+ const [tr, tc] = gridShape(truth2D);
95
+ if (pr !== tr || pc !== tc) return false;
96
+ for (let i = 0; i < tr; i++) {
97
+ for (let j = 0; j < tc; j++) {
98
+ if (pred2D[i][j] !== truth2D[i][j]) return false;
99
+ }
100
+ }
101
+ return true;
102
+ }
103
+
104
+ return JSON.stringify(actualParsed) === JSON.stringify(expectedParsed);
105
+ } catch {
106
+ return actual.trim() === String(expected).trim();
107
+ }
108
+ }
109
+
110
+ function parseCodeFromLLM(response: string): string | null {
111
+ const m = response.match(/```(?:javascript|js|typescript|ts)\s*(.*?)```/s);
112
+ return m ? m[1].trim() : null;
113
+ }
114
+
115
+ function createRNG(seed: number): () => number {
116
+ let s = seed;
117
+ return () => {
118
+ s = (s * 1103515245 + 12345) & 0x7fffffff;
119
+ return s / 0x7fffffff;
120
+ };
121
+ }
122
+
123
+ function formatProblem(
124
+ trainIn: number[][][],
125
+ trainOut: number[][][],
126
+ testIn: number[][][],
127
+ shuffle: boolean = true,
128
+ seed: number = 0
129
+ ): string {
130
+ const indices = trainIn.map((_, i) => i);
131
+ if (shuffle && indices.length > 1) {
132
+ const rng = createRNG(seed);
133
+ for (let i = indices.length - 1; i > 0; i--) {
134
+ const j = Math.floor(rng() * (i + 1));
135
+ [indices[i], indices[j]] = [indices[j], indices[i]];
136
+ }
137
+ }
138
+
139
+ let exampleStr = '';
140
+ let challengeStr = '';
141
+
142
+ for (let e = 0; e < indices.length; e++) {
143
+ const idx = indices[e];
144
+ exampleStr += `\nExample #${e + 1}\nInput:\n<Diagram>\n${gridToDiagram(trainIn[idx])}\n</Diagram>\n\nOutput:\n<Diagram>\n${gridToDiagram(trainOut[idx])}\n</Diagram>\n`;
145
+ }
146
+
147
+ for (let c = 0; c < testIn.length; c++) {
148
+ challengeStr += `\nChallenge #${c + 1}\nInput:\n<Diagram>\n${gridToDiagram(testIn[c])}\n</Diagram>\n`;
149
+ }
150
+
151
+ return exampleStr + challengeStr;
152
+ }
153
+
154
+ interface SolveResult {
155
+ success: boolean;
156
+ output: string;
157
+ softScore: number;
158
+ error: string | null;
159
+ code: string;
160
+ }
161
+
162
+ function buildDetailedFeedback(
163
+ trainResults: SolveResult[],
164
+ _trainInputs: unknown[],
165
+ trainOutputs: unknown[]
166
+ ): string {
167
+ const parts: string[] = [];
168
+
169
+ for (let i = 0; i < trainResults.length; i++) {
170
+ const rr = trainResults[i];
171
+ if (rr.success) {
172
+ parts.push(`Solves Example #${i + 1} correctly. `);
173
+ continue;
174
+ }
175
+
176
+ const msgLines: string[] = [`Solves Example #${i + 1} incorrectly. `];
177
+
178
+ let predArr: unknown = null;
179
+ try {
180
+ if (rr.output) {
181
+ predArr = JSON.parse(rr.output);
182
+ }
183
+ } catch {}
184
+
185
+ const truth = trainOutputs[i];
186
+ const truthArr = Array.isArray(truth) ? truth : null;
187
+
188
+ if (!predArr || !Array.isArray(predArr)) {
189
+ msgLines.push('\nThe output has to be a rectangular grid of numbers.\n');
190
+ if (rr.error) {
191
+ msgLines.push(`Your code produced the following error:\n${rr.error.slice(0, 300)}\n`);
192
+ }
193
+ } else {
194
+ const pred2D = ensure2D(predArr);
195
+ const truth2D = truthArr ? ensure2D(truthArr) : null;
196
+
197
+ if (!truth2D || !pred2D) {
198
+ msgLines.push('\nFailed to parse grids for comparison.\n');
199
+ } else {
200
+ const predShape = gridShape(pred2D);
201
+ const truthShape = gridShape(truth2D);
202
+
203
+ if (predShape[0] !== truthShape[0] || predShape[1] !== truthShape[1]) {
204
+ msgLines.push(
205
+ `\n\nShape mismatch: your prediction's shape was [${predShape}], ` +
206
+ `while the correct shape was [${truthShape}].`
207
+ );
208
+ } else {
209
+ msgLines.push(
210
+ '\nYour code\'s output does not match the expected output.' +
211
+ '\n\nBelow is a visualization of the 2D array your code produced as well as the expected output.\n' +
212
+ 'Correctly predicted values are shown as-is while the incorrectly predicted values are shown ' +
213
+ "in the format 'prediction/correct':\n"
214
+ );
215
+ const diff = arrayDiff(pred2D, truth2D);
216
+ msgLines.push(`\n\`\`\`\n${diff}\n\`\`\`\n`);
217
+ msgLines.push(`Output accuracy: ${rr.softScore.toFixed(2)} (0 is worst, 1 is best).\n`);
218
+ }
219
+ }
220
+
221
+ if (rr.error) {
222
+ msgLines.push(`\n\nYour code produced the following error:\n${rr.error.slice(0, 300)}\n`);
223
+ }
224
+ }
225
+
226
+ parts.push(msgLines.join(''));
227
+ }
228
+
229
+ return parts.join('\n\n');
230
+ }
231
+
232
+ function resolveModelId(modelId: string): { provider: string; id: string } | null {
233
+ const slashIdx = modelId.indexOf('/');
234
+ if (slashIdx === -1) return null;
235
+ return {
236
+ provider: modelId.slice(0, slashIdx),
237
+ id: modelId.slice(slashIdx + 1),
238
+ };
239
+ }
240
+
241
+ function buildFeedbackBlock(
242
+ solutions: Array<{ code: string; feedback: string; score: number }>,
243
+ maxExamples: number = 5,
244
+ improvingOrder: boolean = true
245
+ ): string {
246
+ if (solutions.length === 0) return '';
247
+
248
+ const sorted = [...solutions].sort((a, b) => b.score - a.score);
249
+ const top = sorted.slice(0, maxExamples);
250
+ if (improvingOrder) top.reverse();
251
+
252
+ return top
253
+ .map((s, i) =>
254
+ `<solution_${i + 1}>
255
+ <solution_score>
256
+ ${s.score.toFixed(2)}
257
+ </solution_score>
258
+ </solution_${i + 1}>`
259
+ )
260
+ .join('\n\n');
261
+ }
262
+
263
+ interface StrategyAdaptation {
264
+ insight: string;
265
+ taskType: string;
266
+ models: string[];
267
+ evidenceCount: number;
268
+ timestamp: number;
269
+ promptModifier?: string;
270
+ }
271
+
272
+ interface IterationResult {
273
+ iteration: number;
274
+ expertIndex: number;
275
+ code: string;
276
+ answer: string;
277
+ trainResults: Array<{ success: boolean; softScore: number }>;
278
+ testResults: unknown[];
279
+ passed: boolean;
280
+ score: number;
281
+ feedback: string;
282
+ promptTokens: number;
283
+ completionTokens: number;
284
+ durationMs: number;
285
+ }
286
+
287
+ function learnFromIterations(
288
+ iterations: IterationResult[],
289
+ models: string[],
290
+ taskType: string,
291
+ existingAdaptations: StrategyAdaptation[]
292
+ ): StrategyAdaptation[] {
293
+ const adaptations = [...existingAdaptations];
294
+ const passed = iterations.some((r) => r.passed);
295
+ const bestResult = iterations.reduce<IterationResult | null>(
296
+ (best, r) => (r.score > (best?.score ?? -1) ? r : best),
297
+ null
298
+ );
299
+
300
+ // 1. Successful model tracking
301
+ if (passed && bestResult) {
302
+ const successfulModel = models[bestResult.expertIndex % models.length];
303
+ const existing = adaptations.find(
304
+ (a) => a.taskType === taskType && a.models.includes(successfulModel)
305
+ );
306
+ if (existing) {
307
+ existing.evidenceCount++;
308
+ } else {
309
+ adaptations.push({
310
+ insight: `Model ${successfulModel} successfully solved ${taskType} problems`,
311
+ taskType,
312
+ models: [successfulModel],
313
+ evidenceCount: 1,
314
+ timestamp: Date.now(),
315
+ promptModifier: `Note: Model ${successfulModel} has been effective for ${taskType} tasks.`,
316
+ });
317
+ }
318
+ }
319
+
320
+ // 2. Feedback effectiveness
321
+ if (passed && bestResult && bestResult.iteration > 0) {
322
+ const firstScore = iterations.find(
323
+ (r) => r.expertIndex === bestResult.expertIndex && r.iteration === 0
324
+ )?.score ?? 0;
325
+ const scoreDelta = bestResult.score - firstScore;
326
+ if (scoreDelta > 0.3) {
327
+ const existing = adaptations.find(
328
+ (a) => a.insight.includes('feedback-driven improvement')
329
+ );
330
+ if (existing) {
331
+ existing.evidenceCount++;
332
+ } else {
333
+ adaptations.push({
334
+ insight: 'Feedback-driven improvement is effective',
335
+ taskType: '*',
336
+ models,
337
+ evidenceCount: 1,
338
+ timestamp: Date.now(),
339
+ promptModifier: 'Pay careful attention to feedback from previous attempts.',
340
+ });
341
+ }
342
+ }
343
+ }
344
+
345
+ // 3. Timeout detection
346
+ const timeoutCount = iterations.filter(
347
+ (r) => r.feedback.includes('timeout') || r.feedback.includes('Too many timeouts')
348
+ ).length;
349
+ if (timeoutCount > 2) {
350
+ const existing = adaptations.find((a) => a.insight.includes('performance'));
351
+ if (existing) {
352
+ existing.evidenceCount++;
353
+ } else {
354
+ adaptations.push({
355
+ insight: 'Frequent timeouts suggest solutions need performance optimization',
356
+ taskType,
357
+ models,
358
+ evidenceCount: 1,
359
+ timestamp: Date.now(),
360
+ promptModifier: 'IMPORTANT: Prioritize efficient algorithms.',
361
+ });
362
+ }
363
+ }
364
+
365
+ return adaptations;
366
+ }
367
+
368
+ describe('computeSoftScore', () => {
369
+ it('returns 1.0 for perfect match', () => {
370
+ const actual = JSON.stringify([[1, 2], [3, 4]]);
371
+ const expected = [[1, 2], [3, 4]];
372
+ expect(computeSoftScore(actual, expected)).toBe(1.0);
373
+ });
374
+
375
+ it('returns 0.0 for completely wrong', () => {
376
+ const actual = JSON.stringify([[0, 0], [0, 0]]);
377
+ const expected = [[1, 2], [3, 4]];
378
+ expect(computeSoftScore(actual, expected)).toBe(0.0);
379
+ });
380
+
381
+ it('returns 0.5 for half correct', () => {
382
+ const actual = JSON.stringify([[1, 2], [0, 0]]);
383
+ const expected = [[1, 2], [3, 4]];
384
+ expect(computeSoftScore(actual, expected)).toBe(0.5);
385
+ });
386
+
387
+ it('returns 0 for wrong-length arrays', () => {
388
+ const actual = JSON.stringify([[1, 2]]);
389
+ const expected = [[1, 2], [3, 4]];
390
+ expect(computeSoftScore(actual, expected)).toBe(0);
391
+ });
392
+
393
+ it('returns 0 for invalid JSON', () => {
394
+ expect(computeSoftScore('not json', [[1]])).toBe(0);
395
+ });
396
+
397
+ it('returns 0.25 for one cell correct in 2x2', () => {
398
+ const actual = JSON.stringify([[1, 0], [0, 0]]);
399
+ const expected = [[1, 2], [3, 4]];
400
+ expect(computeSoftScore(actual, expected)).toBe(0.25);
401
+ });
402
+ });
403
+
404
+ describe('compareOutputs', () => {
405
+ it('returns true for identical JSON', () => {
406
+ expect(compareOutputs(JSON.stringify([1, 2, 3]), [1, 2, 3])).toBe(true);
407
+ });
408
+
409
+ it('returns false for different JSON', () => {
410
+ expect(compareOutputs(JSON.stringify([1, 2]), [1, 3])).toBe(false);
411
+ });
412
+
413
+ it('returns true for matching string when JSON parse fails', () => {
414
+ expect(compareOutputs('hello', 'hello')).toBe(true);
415
+ });
416
+
417
+ it('returns false for different strings', () => {
418
+ expect(compareOutputs('hello', 'world')).toBe(false);
419
+ });
420
+ });
421
+
422
+ describe('parseCodeFromLLM', () => {
423
+ it('extracts javascript code block', () => {
424
+ const response = 'Here is my solution:\n```javascript\nfunction transform(grid) {\n return grid;\n}\n```\nDone.';
425
+ expect(parseCodeFromLLM(response)).toBe('function transform(grid) {\n return grid;\n}');
426
+ });
427
+
428
+ it('extracts js code block', () => {
429
+ const response = '```js\nfunction transform(grid) { return grid; }\n```';
430
+ expect(parseCodeFromLLM(response)).toBe('function transform(grid) { return grid; }');
431
+ });
432
+
433
+ it('extracts typescript code block', () => {
434
+ const response = '```typescript\nfunction transform(grid: number[][]) { return grid; }\n```';
435
+ expect(parseCodeFromLLM(response)).toBe('function transform(grid: number[][]) { return grid; }');
436
+ });
437
+
438
+ it('returns null when no code block', () => {
439
+ expect(parseCodeFromLLM('No code here')).toBeNull();
440
+ });
441
+
442
+ it('returns null for non-js code block', () => {
443
+ expect(parseCodeFromLLM('```python\nprint(1)\n```')).toBeNull();
444
+ });
445
+
446
+ it('handles multi-line code', () => {
447
+ const response = '```javascript\nfunction transform(grid) {\n return grid.map(row => row.map(v => v * 2));\n}\n```';
448
+ const parsed = parseCodeFromLLM(response);
449
+ expect(parsed).toContain('function transform');
450
+ expect(parsed).toContain('v * 2');
451
+ });
452
+ });
453
+
454
+ describe('createRNG', () => {
455
+ it('produces deterministic sequence for same seed', () => {
456
+ const rng1 = createRNG(42);
457
+ const rng2 = createRNG(42);
458
+ const seq1 = [rng1(), rng1(), rng1()];
459
+ const seq2 = [rng2(), rng2(), rng2()];
460
+ expect(seq1).toEqual(seq2);
461
+ });
462
+
463
+ it('produces different sequences for different seeds', () => {
464
+ const rng1 = createRNG(0);
465
+ const rng2 = createRNG(100);
466
+ expect(rng1()).not.toBe(rng2());
467
+ });
468
+
469
+ it('produces values between 0 and 1', () => {
470
+ const rng = createRNG(42);
471
+ for (let i = 0; i < 100; i++) {
472
+ const val = rng();
473
+ expect(val).toBeGreaterThanOrEqual(0);
474
+ expect(val).toBeLessThan(1);
475
+ }
476
+ });
477
+ });
478
+
479
+ describe('resolveModelId', () => {
480
+ it('parses provider/id format', () => {
481
+ expect(resolveModelId('anthropic/claude-sonnet-4-5')).toEqual({
482
+ provider: 'anthropic',
483
+ id: 'claude-sonnet-4-5',
484
+ });
485
+ });
486
+
487
+ it('parses openai models', () => {
488
+ expect(resolveModelId('openai/gpt-4o')).toEqual({
489
+ provider: 'openai',
490
+ id: 'gpt-4o',
491
+ });
492
+ });
493
+
494
+ it('returns null for invalid format', () => {
495
+ expect(resolveModelId('just-a-model')).toBeNull();
496
+ });
497
+
498
+ it('handles models with slashes in id', () => {
499
+ expect(resolveModelId('openai/gpt-4o-mini')).toEqual({
500
+ provider: 'openai',
501
+ id: 'gpt-4o-mini',
502
+ });
503
+ });
504
+ });
505
+
506
+ describe('buildFeedbackBlock', () => {
507
+ it('returns empty string for no solutions', () => {
508
+ expect(buildFeedbackBlock([])).toBe('');
509
+ });
510
+
511
+ it('orders solutions by improving order (worst→best)', () => {
512
+ const solutions = [
513
+ { code: 'a', feedback: '', score: 0.9 },
514
+ { code: 'b', feedback: '', score: 0.5 },
515
+ { code: 'c', feedback: '', score: 0.7 },
516
+ ];
517
+ const block = buildFeedbackBlock(solutions, 5, true);
518
+ // Should contain scores in order: 0.5, 0.7, 0.9
519
+ const scores = [...block.matchAll(/(\d+\.\d+)/g)].map((m) => parseFloat(m[1]));
520
+ expect(scores).toEqual([0.5, 0.7, 0.9]);
521
+ });
522
+
523
+ it('orders solutions by decreasing order when improvingOrder=false', () => {
524
+ const solutions = [
525
+ { code: 'a', feedback: '', score: 0.5 },
526
+ { code: 'b', feedback: '', score: 0.9 },
527
+ ];
528
+ const block = buildFeedbackBlock(solutions, 5, false);
529
+ const scores = [...block.matchAll(/(\d+\.\d+)/g)].map((m) => parseFloat(m[1]));
530
+ expect(scores).toEqual([0.9, 0.5]);
531
+ });
532
+
533
+ it('limits to maxExamples', () => {
534
+ const solutions = Array.from({ length: 10 }, (_, i) => ({
535
+ code: `c${i}`,
536
+ feedback: '',
537
+ score: i / 10,
538
+ }));
539
+ const block = buildFeedbackBlock(solutions, 3, true);
540
+ // Count opening tags only (not closing </solution_>)
541
+ const count = (block.match(/<solution_\d+>/g) || []).length;
542
+ expect(count).toBe(3);
543
+ });
544
+ });
545
+
546
+ describe('learnFromIterations', () => {
547
+ it('learns from successful model', () => {
548
+ const iterations: IterationResult[] = [
549
+ {
550
+ iteration: 0,
551
+ expertIndex: 0,
552
+ code: 'def f(): pass',
553
+ answer: '',
554
+ trainResults: [{ success: true, softScore: 1.0 }],
555
+ testResults: [],
556
+ passed: true,
557
+ score: 1.0,
558
+ feedback: '',
559
+ promptTokens: 100,
560
+ completionTokens: 200,
561
+ durationMs: 5000,
562
+ },
563
+ ];
564
+
565
+ const adaptations = learnFromIterations(iterations, ['anthropic/claude-sonnet-4-5'], 'code-reasoning', []);
566
+ expect(adaptations).toHaveLength(1);
567
+ expect(adaptations[0].insight).toContain('anthropic/claude-sonnet-4-5');
568
+ expect(adaptations[0].taskType).toBe('code-reasoning');
569
+ });
570
+
571
+ it('increments evidence for repeated model success', () => {
572
+ const iterations: IterationResult[] = [
573
+ {
574
+ iteration: 0,
575
+ expertIndex: 0,
576
+ code: 'pass',
577
+ answer: '',
578
+ trainResults: [{ success: true, softScore: 1.0 }],
579
+ testResults: [],
580
+ passed: true,
581
+ score: 1.0,
582
+ feedback: '',
583
+ promptTokens: 0,
584
+ completionTokens: 0,
585
+ durationMs: 0,
586
+ },
587
+ ];
588
+
589
+ const existing: StrategyAdaptation[] = [{
590
+ insight: 'Model anthropic/claude-sonnet-4-5 successfully solved code-reasoning problems',
591
+ taskType: 'code-reasoning',
592
+ models: ['anthropic/claude-sonnet-4-5'],
593
+ evidenceCount: 2,
594
+ timestamp: Date.now(),
595
+ promptModifier: 'Note.',
596
+ }];
597
+
598
+ const adaptations = learnFromIterations(iterations, ['anthropic/claude-sonnet-4-5'], 'code-reasoning', existing);
599
+ const match = adaptations.find((a) => a.taskType === 'code-reasoning');
600
+ expect(match?.evidenceCount).toBe(3);
601
+ });
602
+
603
+ it('detects feedback-driven improvement', () => {
604
+ const iterations: IterationResult[] = [
605
+ {
606
+ iteration: 0,
607
+ expertIndex: 0,
608
+ code: 'pass',
609
+ answer: '',
610
+ trainResults: [{ success: false, softScore: 0.1 }],
611
+ testResults: [],
612
+ passed: false,
613
+ score: 0.1,
614
+ feedback: '',
615
+ promptTokens: 0,
616
+ completionTokens: 0,
617
+ durationMs: 0,
618
+ },
619
+ {
620
+ iteration: 1,
621
+ expertIndex: 0,
622
+ code: 'pass',
623
+ answer: '',
624
+ trainResults: [{ success: true, softScore: 1.0 }],
625
+ testResults: [],
626
+ passed: true,
627
+ score: 1.0,
628
+ feedback: '',
629
+ promptTokens: 0,
630
+ completionTokens: 0,
631
+ durationMs: 0,
632
+ },
633
+ ];
634
+
635
+ const adaptations = learnFromIterations(iterations, ['openai/gpt-4o'], 'code-reasoning', []);
636
+ // Should have a model success adaptation and a feedback-driven improvement adaptation
637
+ const feedbackAdaptation = adaptations.find((a) => a.insight.includes('Feedback-driven improvement'));
638
+ expect(feedbackAdaptation).toBeDefined();
639
+ expect(feedbackAdaptation?.taskType).toBe('*');
640
+ });
641
+
642
+ it('detects timeout patterns', () => {
643
+ const iterations: IterationResult[] = Array.from({ length: 3 }, (_, i) => ({
644
+ iteration: i,
645
+ expertIndex: 0,
646
+ code: 'pass',
647
+ answer: '',
648
+ trainResults: [{ success: false, softScore: 0 }],
649
+ testResults: [],
650
+ passed: false,
651
+ score: 0,
652
+ feedback: 'Too many timeouts. Code may have infinite loop.',
653
+ promptTokens: 0,
654
+ completionTokens: 0,
655
+ durationMs: 0,
656
+ }));
657
+
658
+ const adaptations = learnFromIterations(iterations, ['openai/gpt-4o'], 'code-reasoning', []);
659
+ const perfAdaptation = adaptations.find((a) => a.insight.includes('performance'));
660
+ expect(perfAdaptation).toBeDefined();
661
+ expect(perfAdaptation?.promptModifier).toContain('efficient algorithms');
662
+ });
663
+
664
+ it('does not add feedback adaptation when improvement is small', () => {
665
+ const iterations: IterationResult[] = [
666
+ {
667
+ iteration: 0,
668
+ expertIndex: 0,
669
+ code: 'pass',
670
+ answer: '',
671
+ trainResults: [{ success: false, softScore: 0.8 }],
672
+ testResults: [],
673
+ passed: false,
674
+ score: 0.8,
675
+ feedback: '',
676
+ promptTokens: 0,
677
+ completionTokens: 0,
678
+ durationMs: 0,
679
+ },
680
+ {
681
+ iteration: 1,
682
+ expertIndex: 0,
683
+ code: 'pass',
684
+ answer: '',
685
+ trainResults: [{ success: true, softScore: 1.0 }],
686
+ testResults: [],
687
+ passed: true,
688
+ score: 1.0,
689
+ feedback: '',
690
+ promptTokens: 0,
691
+ completionTokens: 0,
692
+ durationMs: 0,
693
+ },
694
+ ];
695
+
696
+ const adaptations = learnFromIterations(iterations, ['openai/gpt-4o'], 'code-reasoning', []);
697
+ const feedbackAdaptation = adaptations.find((a) => a.insight.includes('feedback-driven improvement'));
698
+ // Score delta is 0.2, which is < 0.3 threshold
699
+ expect(feedbackAdaptation).toBeUndefined();
700
+ });
701
+ });
702
+
703
+ describe('voting algorithm (simulated)', () => {
704
+ // Simplified voting test with mock data
705
+ it('ranks passing solutions before failing ones', () => {
706
+ type SimpleResult = { key: string; passed: boolean; score: number };
707
+
708
+ const results: SimpleResult[] = [
709
+ { key: 'A', passed: false, score: 0.5 },
710
+ { key: 'B', passed: true, score: 1.0 },
711
+ { key: 'C', passed: false, score: 0.3 },
712
+ ];
713
+
714
+ // Simple ranking: passers first, then failures sorted by score desc
715
+ const ranked = results.sort((a, b) => {
716
+ if (a.passed && !b.passed) return -1;
717
+ if (!a.passed && b.passed) return 1;
718
+ return b.score - a.score;
719
+ });
720
+
721
+ expect(ranked[0].key).toBe('B');
722
+ expect(ranked[1].key).toBe('A');
723
+ expect(ranked[2].key).toBe('C');
724
+ });
725
+
726
+ it('groups by output and sorts by vote count', () => {
727
+ const outputs = [
728
+ { output: 'X', passed: true },
729
+ { output: 'X', passed: true },
730
+ { output: 'Y', passed: true },
731
+ { output: 'Z', passed: false },
732
+ ];
733
+
734
+ const groups = new Map<string, number>();
735
+ for (const o of outputs) {
736
+ if (o.passed) {
737
+ groups.set(o.output, (groups.get(o.output) || 0) + 1);
738
+ }
739
+ }
740
+
741
+ const sorted = [...groups.entries()].sort((a, b) => b[1] - a[1]);
742
+ expect(sorted[0]).toEqual(['X', 2]);
743
+ expect(sorted[1]).toEqual(['Y', 1]);
744
+ });
745
+ });
746
+
747
+ describe('budget tracking', () => {
748
+ it('stops when cost budget exceeded', () => {
749
+ const budget = { maxCost: 0.01, costSoFar: 0 };
750
+ let iterations = 0;
751
+
752
+ while (budget.costSoFar < (budget.maxCost ?? Infinity) && iterations < 100) {
753
+ budget.costSoFar += 0.005;
754
+ iterations++;
755
+ }
756
+
757
+ expect(iterations).toBe(2); // 0.005 + 0.005 = 0.01
758
+ });
759
+
760
+ it('stops when time budget exceeded', () => {
761
+ const startTime = Date.now() - 5000; // 5 seconds ago
762
+ const maxTime = 3; // 3 seconds
763
+ const elapsed = (Date.now() - startTime) / 1000;
764
+
765
+ expect(elapsed).toBeGreaterThan(maxTime);
766
+ });
767
+ });
768
+
769
+ describe('vm sandbox', () => {
770
+ // We replicate the vm sandbox logic here for testing
771
+ async function runInSandbox(code: string, input: unknown, timeoutS: number = 5): Promise<{ ok: boolean; output: string; timedOut: boolean }> {
772
+ const vm = await import('node:vm');
773
+ try {
774
+ const context = vm.createContext({
775
+ console: { log: () => {}, error: () => {}, warn: () => {} },
776
+ Math,
777
+ JSON,
778
+ Array,
779
+ Object,
780
+ String,
781
+ Number,
782
+ Boolean,
783
+ Date,
784
+ Map,
785
+ Set,
786
+ parseInt,
787
+ parseFloat,
788
+ isNaN,
789
+ isFinite,
790
+ RegExp,
791
+ Error,
792
+ TypeError,
793
+ RangeError,
794
+ __input__: input,
795
+ __output__: null,
796
+ });
797
+
798
+ const wrappedCode = `
799
+ ${code}
800
+
801
+ if (typeof transform === 'function') {
802
+ try {
803
+ __output__ = transform(__input__);
804
+ } catch (e) {
805
+ __output__ = { __error__: e.message || String(e) };
806
+ }
807
+ }
808
+ `;
809
+
810
+ const script = new vm.Script(wrappedCode, { filename: 'sandbox.js' });
811
+ script.runInContext(context, { timeout: timeoutS * 1000 });
812
+
813
+ const result = context.__output__;
814
+ if (result && typeof result === 'object' && result.__error__) {
815
+ return { ok: false, output: result.__error__, timedOut: false };
816
+ }
817
+ return { ok: true, output: JSON.stringify(result), timedOut: false };
818
+ } catch (e: any) {
819
+ const isTimeout = e.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT' || (e.message && e.message.includes('timeout'));
820
+ return { ok: false, output: isTimeout ? 'timeout' : (e.message || String(e)), timedOut: isTimeout };
821
+ }
822
+ }
823
+
824
+ it('executes a simple transform function', async () => {
825
+ const code = 'function transform(grid) { return grid.map(row => row.map(v => v * 2)); }';
826
+ const result = await runInSandbox(code, [[1, 2], [3, 4]]);
827
+ expect(result.ok).toBe(true);
828
+ expect(JSON.parse(result.output)).toEqual([[2, 4], [6, 8]]);
829
+ });
830
+
831
+ it('catches runtime errors', async () => {
832
+ const code = 'function transform(grid) { return grid.foo.bar; }';
833
+ const result = await runInSandbox(code, [[1]]);
834
+ expect(result.ok).toBe(false);
835
+ expect(result.output).toContain('Cannot read');
836
+ });
837
+
838
+ it('catches syntax errors', async () => {
839
+ const code = 'function transform(grid { return grid; }'; // missing closing paren
840
+ const result = await runInSandbox(code, [[1]]);
841
+ expect(result.ok).toBe(false);
842
+ });
843
+
844
+ it('detects timeouts', async () => {
845
+ const code = 'function transform(grid) { while(true) {} }';
846
+ const result = await runInSandbox(code, [[1]], 1); // 1 second timeout
847
+ expect(result.timedOut).toBe(true);
848
+ });
849
+
850
+ it('provides standard JS builtins', async () => {
851
+ const code = 'function transform(grid) { return grid.flat().sort((a,b) => a - b); }';
852
+ const result = await runInSandbox(code, [[3, 1], [2, 4]]);
853
+ expect(result.ok).toBe(true);
854
+ expect(JSON.parse(result.output)).toEqual([1, 2, 3, 4]);
855
+ });
856
+
857
+ it('isolates the sandbox from Node globals', async () => {
858
+ const code = 'function transform(grid) { return typeof process; }';
859
+ const result = await runInSandbox(code, [[1]]);
860
+ expect(result.ok).toBe(true);
861
+ expect(JSON.parse(result.output)).toBe('undefined');
862
+ });
863
+ });
864
+
865
+ describe('formatProblem', () => {
866
+ it('formats grids into <Diagram> text', () => {
867
+ const result = formatProblem(
868
+ [[[1, 2], [3, 4]]], // trainIn
869
+ [[[5, 6], [7, 8]]], // trainOut
870
+ [[[9, 10]]], // testIn
871
+ false, // shuffle
872
+ 0 // seed
873
+ );
874
+ expect(result).toContain('<Diagram>');
875
+ expect(result).toContain('Example #1');
876
+ expect(result).toContain('Challenge #1');
877
+ expect(result).toContain('1 2');
878
+ expect(result).toContain('5 6');
879
+ expect(result).toContain('9 10');
880
+ });
881
+
882
+ it('shuffles training examples with different seeds', () => {
883
+ const trainIn = [[[1]], [[2]], [[3]], [[4]], [[5]]];
884
+ const trainOut = [[[10]], [[20]], [[30]], [[40]], [[50]]];
885
+
886
+ const result1 = formatProblem(trainIn, trainOut, [], true, 0);
887
+ const result2 = formatProblem(trainIn, trainOut, [], true, 42);
888
+
889
+ // Same seed should produce same order
890
+ const result1b = formatProblem(trainIn, trainOut, [], true, 0);
891
+ expect(result1).toBe(result1b);
892
+
893
+ // Different seeds *may* produce different order (probabilistic, but very likely with 5 items)
894
+ // Just verify they're both valid
895
+ expect(result1).toContain('<Diagram>');
896
+ expect(result2).toContain('<Diagram>');
897
+ });
898
+
899
+ it('handles single training example (no shuffle possible)', () => {
900
+ const result = formatProblem([[[0]]], [[[1]]], [], false, 0);
901
+ expect(result).toContain('Example #1');
902
+ expect(result).toContain('0');
903
+ expect(result).toContain('1');
904
+ });
905
+ });
906
+
907
+ describe('arrayDiff', () => {
908
+ it('shows matching values as-is, mismatches as pred/truth', () => {
909
+ const pred = [[1, 2], [3, 4]];
910
+ const truth = [[1, 9], [3, 8]];
911
+ const diff = arrayDiff(pred, truth);
912
+ expect(diff).toContain('1'); // match
913
+ expect(diff).toContain('2/9'); // mismatch
914
+ expect(diff).toContain('3'); // match
915
+ expect(diff).toContain('4/8'); // mismatch
916
+ });
917
+
918
+ it('handles fully matching grids', () => {
919
+ const diff = arrayDiff([[1, 2]], [[1, 2]]);
920
+ expect(diff).toBe('1 2');
921
+ });
922
+ });
923
+
924
+ describe('buildDetailedFeedback (Poetiq parity)', () => {
925
+ it('reports shape mismatch when dimensions differ', () => {
926
+ const trainResults: SolveResult[] = [
927
+ { success: false, output: '[[1,2]]', softScore: 0, error: null, code: '' },
928
+ ];
929
+ const trainOutputs = [[[1, 2], [3, 4]]];
930
+
931
+ const feedback = buildDetailedFeedback(trainResults, [], trainOutputs);
932
+ expect(feedback).toContain('Shape mismatch');
933
+ });
934
+
935
+ it('shows diff grid when shapes match but values differ', () => {
936
+ const trainResults: SolveResult[] = [
937
+ { success: false, output: '[[1,9],[3,8]]', softScore: 0.5, error: null, code: '' },
938
+ ];
939
+ const trainOutputs = [[[1, 2], [3, 4]]];
940
+
941
+ const feedback = buildDetailedFeedback(trainResults, [], trainOutputs);
942
+ expect(feedback).toContain('9/2');
943
+ expect(feedback).toContain('8/4');
944
+ expect(feedback).toContain('0.50');
945
+ });
946
+
947
+ it('reports bad JSON output', () => {
948
+ const trainResults: SolveResult[] = [
949
+ { success: false, output: 'not json', softScore: 0, error: 'parse error', code: '' },
950
+ ];
951
+ const trainOutputs = [[[1]]];
952
+
953
+ const feedback = buildDetailedFeedback(trainResults, [], trainOutputs);
954
+ expect(feedback).toContain('rectangular grid');
955
+ });
956
+
957
+ it('reports execution errors', () => {
958
+ const trainResults: SolveResult[] = [
959
+ { success: false, output: '', softScore: 0, error: 'TypeError: Cannot read properties of undefined', code: '' },
960
+ ];
961
+ const trainOutputs = [[[1]]];
962
+
963
+ const feedback = buildDetailedFeedback(trainResults, [], trainOutputs);
964
+ expect(feedback).toContain('TypeError');
965
+ });
966
+ });
967
+
968
+ describe('PromptDelta', () => {
969
+ const { applyPromptDelta } = (() => {
970
+ // Inline applyPromptDelta for testing
971
+ function applyPromptDelta(basePrompt: string, delta: any): string {
972
+ let result = basePrompt;
973
+ for (const [section, replacement] of Object.entries(delta.sectionReplacements || {})) {
974
+ result = result.replace(section, replacement as string);
975
+ }
976
+ if (delta.preProblemInsert) {
977
+ const problemIdx = result.indexOf('$$problem$$');
978
+ if (problemIdx !== -1) {
979
+ result = result.slice(0, problemIdx) +
980
+ '\n\n**Problem-Specific Strategy:**\n' + delta.preProblemInsert + '\n\n' +
981
+ result.slice(problemIdx);
982
+ }
983
+ }
984
+ if (delta.postProblemInsert) {
985
+ result = result.replace('$$problem$$', () => '$$problem$$\n\n**Critical Reminders:**\n' + delta.postProblemInsert);
986
+ }
987
+ if (delta.antiPatterns && delta.antiPatterns.length > 0) {
988
+ result += '\n\n**DO NOT:**\n' + delta.antiPatterns.map((a: string, i: number) => `${i + 1}. ${a}`).join('\n');
989
+ }
990
+ if (delta.additionalExamples && delta.additionalExamples.length > 0) {
991
+ const examplesStr = delta.additionalExamples
992
+ .map((e: any, i: number) => `**Custom Example ${i + 1}:**\nProblem: ${e.problem}\nSolution: ${e.solution}`)
993
+ .join('\n\n');
994
+ result = result.replace('$$problem$$', () => examplesStr + '\n\n$$problem$$');
995
+ }
996
+ return result;
997
+ }
998
+ return { applyPromptDelta };
999
+ })();
1000
+
1001
+ it('applies preProblemInsert before $$problem$$', () => {
1002
+ const base = 'Hello $$problem$$ goodbye';
1003
+ const delta = { preProblemInsert: 'STRATEGY HINT', postProblemInsert: null, sectionReplacements: {}, additionalExamples: [], antiPatterns: [] };
1004
+ const result = applyPromptDelta(base, delta);
1005
+ expect(result).toContain('**Problem-Specific Strategy:**');
1006
+ expect(result).toContain('STRATEGY HINT');
1007
+ expect(result.indexOf('STRATEGY HINT')).toBeLessThan(result.indexOf('$$problem$$'));
1008
+ });
1009
+
1010
+ it('applies postProblemInsert after $$problem$$', () => {
1011
+ const base = 'Hello $$problem$$ goodbye';
1012
+ const delta = { preProblemInsert: null, postProblemInsert: 'NO CONSOLE.LOG', sectionReplacements: {}, additionalExamples: [], antiPatterns: [] };
1013
+ const result = applyPromptDelta(base, delta);
1014
+ expect(result).toContain('**Critical Reminders:**');
1015
+ expect(result).toContain('NO CONSOLE.LOG');
1016
+ });
1017
+
1018
+ it('appends anti-patterns', () => {
1019
+ const base = 'Hello $$problem$$';
1020
+ const delta = { preProblemInsert: null, postProblemInsert: null, sectionReplacements: {}, additionalExamples: [], antiPatterns: ['No brute force', 'No hardcoded values'] };
1021
+ const result = applyPromptDelta(base, delta);
1022
+ expect(result).toContain('**DO NOT:**');
1023
+ expect(result).toContain('No brute force');
1024
+ expect(result).toContain('No hardcoded values');
1025
+ });
1026
+
1027
+ it('inserts additional examples before $$problem$$', () => {
1028
+ const base = 'Hello $$problem$$';
1029
+ const delta = {
1030
+ preProblemInsert: null, postProblemInsert: null, sectionReplacements: {},
1031
+ additionalExamples: [{ problem: 'rotate grid', solution: 'use transpose' }],
1032
+ antiPatterns: [],
1033
+ };
1034
+ const result = applyPromptDelta(base, delta);
1035
+ expect(result).toContain('Custom Example 1');
1036
+ expect(result).toContain('rotate grid');
1037
+ });
1038
+
1039
+ it('applies section replacements', () => {
1040
+ const base = 'Old text $$problem$$';
1041
+ const delta = {
1042
+ preProblemInsert: null, postProblemInsert: null,
1043
+ sectionReplacements: { 'Old text': 'New text' },
1044
+ additionalExamples: [], antiPatterns: [],
1045
+ };
1046
+ const result = applyPromptDelta(base, delta);
1047
+ expect(result).toContain('New text');
1048
+ expect(result).not.toContain('Old text');
1049
+ });
1050
+
1051
+ it('combines all delta types', () => {
1052
+ const base = 'Start $$problem$$ End';
1053
+ const delta = {
1054
+ preProblemInsert: 'HINT',
1055
+ postProblemInsert: 'REMINDER',
1056
+ sectionReplacements: { Start: 'Beginning' },
1057
+ additionalExamples: [{ problem: 'p', solution: 's' }],
1058
+ antiPatterns: ['no x'],
1059
+ };
1060
+ const result = applyPromptDelta(base, delta);
1061
+ expect(result).toContain('HINT');
1062
+ expect(result).toContain('REMINDER');
1063
+ expect(result).toContain('Beginning');
1064
+ expect(result).toContain('Custom Example');
1065
+ expect(result).toContain('no x');
1066
+ });
1067
+
1068
+ it('leaves prompt unchanged with empty delta', () => {
1069
+ const base = 'Hello $$problem$$';
1070
+ const delta = { preProblemInsert: null, postProblemInsert: null, sectionReplacements: {}, additionalExamples: [], antiPatterns: [] };
1071
+ const result = applyPromptDelta(base, delta);
1072
+ expect(result).toBe(base);
1073
+ });
1074
+ });
1075
+
1076
+ describe('Budget bandit', () => {
1077
+ const { shouldStopEarly, shouldReExplore } = (() => {
1078
+ function shouldStopEarly(history: Array<{ score: number; passed: boolean }>, minIterations = 3) {
1079
+ if (history.length < minIterations) return { stop: false, reason: '' };
1080
+ const last3 = history.slice(-3);
1081
+ const allFailed = last3.every((r) => !r.passed);
1082
+ const noProgress = last3.every((r) => r.score === last3[0].score) && last3[0].score < 0.5;
1083
+ if (allFailed && noProgress) return { stop: true, reason: `No progress after ${history.length} iterations` };
1084
+ if (history.length >= 4) {
1085
+ const last4 = history.slice(-4);
1086
+ const decreasing = last4.every((r, i) => i === 0 || r.score <= last4[i - 1].score);
1087
+ if (decreasing && last4[3].score < 0.3) return { stop: true, reason: 'Score decreasing' };
1088
+ }
1089
+ return { stop: false, reason: '' };
1090
+ }
1091
+
1092
+ function shouldReExplore(allResults: any[][], totalIterations: number) {
1093
+ const allStuck = allResults.every((results) => {
1094
+ const last3 = results.slice(-3);
1095
+ return last3.length >= 3 && last3.every((r: any) => r.score === 0);
1096
+ });
1097
+ if (allStuck && totalIterations >= 5) return { reExplore: true, reason: 'All experts stuck' };
1098
+ return { reExplore: false, reason: '' };
1099
+ }
1100
+
1101
+ return { shouldStopEarly, shouldReExplore };
1102
+ })();
1103
+
1104
+ it('stops early when score is stuck at 0', () => {
1105
+ const history = [
1106
+ { score: 0, passed: false },
1107
+ { score: 0, passed: false },
1108
+ { score: 0, passed: false },
1109
+ ];
1110
+ const result = shouldStopEarly(history);
1111
+ expect(result.stop).toBe(true);
1112
+ });
1113
+
1114
+ it('does not stop early with fewer than 3 iterations', () => {
1115
+ const history = [
1116
+ { score: 0, passed: false },
1117
+ { score: 0, passed: false },
1118
+ ];
1119
+ const result = shouldStopEarly(history);
1120
+ expect(result.stop).toBe(false);
1121
+ });
1122
+
1123
+ it('does not stop when making progress', () => {
1124
+ const history = [
1125
+ { score: 0.2, passed: false },
1126
+ { score: 0.5, passed: false },
1127
+ { score: 0.7, passed: false },
1128
+ ];
1129
+ const result = shouldStopEarly(history);
1130
+ expect(result.stop).toBe(false);
1131
+ });
1132
+
1133
+ it('does not stop when stuck at high score', () => {
1134
+ const history = [
1135
+ { score: 0.8, passed: false },
1136
+ { score: 0.8, passed: false },
1137
+ { score: 0.8, passed: false },
1138
+ ];
1139
+ const result = shouldStopEarly(history);
1140
+ expect(result.stop).toBe(false); // 0.8 >= 0.5
1141
+ });
1142
+
1143
+ it('stops when score is decreasing', () => {
1144
+ const history = [
1145
+ { score: 0.3, passed: false },
1146
+ { score: 0.2, passed: false },
1147
+ { score: 0.1, passed: false },
1148
+ { score: 0.0, passed: false },
1149
+ ];
1150
+ const result = shouldStopEarly(history);
1151
+ expect(result.stop).toBe(true);
1152
+ });
1153
+
1154
+ it('triggers re-explore when all experts are stuck', () => {
1155
+ const allResults = [
1156
+ [{ score: 0 }, { score: 0 }, { score: 0 }],
1157
+ [{ score: 0 }, { score: 0 }, { score: 0 }],
1158
+ ];
1159
+ const result = shouldReExplore(allResults, 6);
1160
+ expect(result.reExplore).toBe(true);
1161
+ });
1162
+
1163
+ it('does not re-explore when an expert is making progress', () => {
1164
+ const allResults = [
1165
+ [{ score: 0 }, { score: 0.5 }, { score: 0.8 }],
1166
+ [{ score: 0 }, { score: 0 }, { score: 0 }],
1167
+ ];
1168
+ const result = shouldReExplore(allResults, 6);
1169
+ expect(result.reExplore).toBe(false);
1170
+ });
1171
+ });
1172
+
1173
+ describe('Thompson sampling', () => {
1174
+ it('returns the only available model', () => {
1175
+ // We can't easily test the real thompsonSampleModel without mocking,
1176
+ // so test the Beta sampling primitives
1177
+ const { betaSample, gammaVariate, randn } = (() => {
1178
+ function randn() {
1179
+ const u1 = Math.random();
1180
+ const u2 = Math.random();
1181
+ return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
1182
+ }
1183
+ function gammaVariate(shape: number): number {
1184
+ if (shape < 1) return gammaVariate(shape + 1) * Math.pow(Math.random(), 1 / shape);
1185
+ const d = shape - 1 / 3;
1186
+ const c = 1 / Math.sqrt(9 * d);
1187
+ while (true) {
1188
+ let x, v;
1189
+ do { x = randn(); v = 1 + c * x; } while (v <= 0);
1190
+ v = v * v * v;
1191
+ const u = Math.random();
1192
+ if (u < 1 - 0.0331 * (x * x) * (x * x)) return d * v;
1193
+ if (Math.log(u) < 0.5 * x * x + d * (1 - v + Math.log(v))) return d * v;
1194
+ }
1195
+ }
1196
+ function betaSample(alpha: number, beta: number): number {
1197
+ const x = gammaVariate(alpha);
1198
+ const y = gammaVariate(beta);
1199
+ return x / (x + y);
1200
+ }
1201
+ return { betaSample, gammaVariate, randn };
1202
+ })();
1203
+
1204
+ // Beta(1,1) should produce uniform-ish values
1205
+ const samples = Array.from({ length: 100 }, () => betaSample(1, 1));
1206
+ const mean = samples.reduce((a, b) => a + b, 0) / samples.length;
1207
+ expect(mean).toBeGreaterThan(0.2);
1208
+ expect(mean).toBeLessThan(0.8);
1209
+
1210
+ // Beta(10,1) should produce values near 1
1211
+ const highAlpha = Array.from({ length: 100 }, () => betaSample(10, 1));
1212
+ const highMean = highAlpha.reduce((a, b) => a + b, 0) / highAlpha.length;
1213
+ expect(highMean).toBeGreaterThan(0.7);
1214
+
1215
+ // Beta(1,10) should produce values near 0
1216
+ const highBeta = Array.from({ length: 100 }, () => betaSample(1, 10));
1217
+ const lowMean = highBeta.reduce((a, b) => a + b, 0) / highBeta.length;
1218
+ expect(lowMean).toBeLessThan(0.3);
1219
+ });
1220
+ });
1221
+
1222
+ describe('Meta-rule engine', () => {
1223
+ it('validates meta-rules by category', () => {
1224
+ // Simulate the validation function inline
1225
+ function validateMetaRule(rule: any, category: string, improved: boolean) {
1226
+ rule.testCount++;
1227
+ if (improved) rule.improvementCount++;
1228
+ if (!rule.validatedCategories.includes(category)) rule.validatedCategories.push(category);
1229
+ rule.lastValidated = Date.now();
1230
+ }
1231
+
1232
+ const rule = {
1233
+ id: 'test',
1234
+ principle: 'Add worked examples',
1235
+ validatedCategories: [],
1236
+ improvementCount: 0,
1237
+ testCount: 0,
1238
+ suggestedDelta: {},
1239
+ sourceStrategyId: null,
1240
+ created: Date.now(),
1241
+ lastValidated: 0,
1242
+ };
1243
+
1244
+ validateMetaRule(rule, 'grid-transformation', true);
1245
+ expect(rule.testCount).toBe(1);
1246
+ expect(rule.improvementCount).toBe(1);
1247
+ expect(rule.validatedCategories).toContain('grid-transformation');
1248
+
1249
+ validateMetaRule(rule, 'knowledge-synthesis', false);
1250
+ expect(rule.testCount).toBe(2);
1251
+ expect(rule.improvementCount).toBe(1);
1252
+ expect(rule.validatedCategories).toContain('knowledge-synthesis');
1253
+ });
1254
+
1255
+ it('applies meta-rules filtered by category and freshness', () => {
1256
+ // Test the filtering logic
1257
+ const rules = [
1258
+ {
1259
+ id: 'r1', principle: 'Test', validatedCategories: ['grid-transformation'],
1260
+ improvementCount: 2, testCount: 3, lastValidated: Date.now(),
1261
+ suggestedDelta: { preProblemInsert: 'grid hint' },
1262
+ },
1263
+ {
1264
+ id: 'r2', principle: 'Universal', validatedCategories: [],
1265
+ improvementCount: 1, testCount: 2, lastValidated: Date.now(),
1266
+ suggestedDelta: { preProblemInsert: 'universal hint' },
1267
+ },
1268
+ {
1269
+ id: 'r3', principle: 'Stale', validatedCategories: ['grid-transformation'],
1270
+ improvementCount: 0, testCount: 10, lastValidated: 0, // stale
1271
+ suggestedDelta: { preProblemInsert: 'stale hint' },
1272
+ },
1273
+ ];
1274
+
1275
+ const category = 'grid-transformation';
1276
+ const STALE_MS = 7 * 24 * 60 * 60 * 1000;
1277
+ const now = Date.now();
1278
+
1279
+ const relevant = rules.filter((r) => {
1280
+ const isCategoryMatch = r.validatedCategories.includes(category) || r.validatedCategories.length === 0;
1281
+ const isFresh = now - r.lastValidated < STALE_MS || r.lastValidated === 0;
1282
+ const hasPositiveEvidence = r.improvementCount > 0 || r.testCount < 5;
1283
+ return isCategoryMatch && isFresh && hasPositiveEvidence;
1284
+ });
1285
+
1286
+ // r1 matches category and is fresh with positive evidence
1287
+ expect(relevant.some(r => r.id === 'r1')).toBe(true);
1288
+ // r2 is universal (empty categories) and fresh
1289
+ expect(relevant.some(r => r.id === 'r2')).toBe(true);
1290
+ // r3 has testCount=10 with 0 improvements and lastValidated=0 → no positive evidence
1291
+ expect(relevant.some(r => r.id === 'r3')).toBe(false);
1292
+ });
1293
+ });
1294
+
1295
+ describe('Prompt quality metrics', () => {
1296
+ it('tracks code parse rate and sandbox success rate', () => {
1297
+ function recordPromptQuality(metrics: any, codeParsed: boolean, sandboxOk: boolean, firstIterScore: number) {
1298
+ const n = metrics.observationCount;
1299
+ metrics.codeParseRate = (metrics.codeParseRate * n + (codeParsed ? 1 : 0)) / (n + 1);
1300
+ metrics.sandboxSuccessRate = (metrics.sandboxSuccessRate * n + (sandboxOk ? 1 : 0)) / (n + 1);
1301
+ metrics.avgFirstIterationScore = (metrics.avgFirstIterationScore * n + firstIterScore) / (n + 1);
1302
+ metrics.observationCount = n + 1;
1303
+ }
1304
+
1305
+ const metrics = { codeParseRate: 0, sandboxSuccessRate: 0, avgFirstIterationScore: 0, observationCount: 0 };
1306
+
1307
+ recordPromptQuality(metrics, true, true, 0.8);
1308
+ expect(metrics.observationCount).toBe(1);
1309
+ expect(metrics.codeParseRate).toBe(1);
1310
+ expect(metrics.sandboxSuccessRate).toBe(1);
1311
+ expect(metrics.avgFirstIterationScore).toBe(0.8);
1312
+
1313
+ recordPromptQuality(metrics, false, true, 0.4);
1314
+ expect(metrics.observationCount).toBe(2);
1315
+ expect(metrics.codeParseRate).toBe(0.5);
1316
+ expect(metrics.sandboxSuccessRate).toBe(1);
1317
+ expect(metrics.avgFirstIterationScore).toBeCloseTo(0.6, 10);
1318
+ });
1319
+ });
1320
+
1321
+ describe('Harness spec generation', () => {
1322
+ it('creates specs with correct types', () => {
1323
+ const spec = {
1324
+ id: 'test1',
1325
+ category: 'grid-transformation',
1326
+ approach: 'code-sandbox' as const,
1327
+ solverPrompt: 'Solve $$problem$$',
1328
+ feedbackPrompt: 'Feedback $$feedback$$',
1329
+ configOverrides: { temperature: 0.8 },
1330
+ validationScore: 0,
1331
+ validationTests: 0,
1332
+ validated: false,
1333
+ parentId: null,
1334
+ generation: 0,
1335
+ created: Date.now(),
1336
+ useCount: 0,
1337
+ successCount: 0,
1338
+ avgScore: 0,
1339
+ };
1340
+ expect(spec.approach).toBe('code-sandbox');
1341
+ expect(spec.solverPrompt).toContain('$$problem$$');
1342
+ });
1343
+
1344
+ it('supports multiple approach types', () => {
1345
+ const approaches = ['code-sandbox', 'decomposition', 'chain-of-questions', 'analogy', 'counter-factual', 'exhaustive-search'] as const;
1346
+ expect(approaches.length).toBe(6);
1347
+ for (const a of approaches) {
1348
+ expect(typeof a).toBe('string');
1349
+ }
1350
+ });
1351
+ });
1352
+
1353
+ describe('Ensemble diversification', () => {
1354
+ it('assigns different approaches per expert', () => {
1355
+ const APPROACH_SOLVER_PROMPTS: Record<string, string> = {
1356
+ 'code-sandbox': 'Code approach $$problem$$',
1357
+ 'decomposition': 'Decompose $$problem$$',
1358
+ 'analogy': 'Analogy $$problem$$',
1359
+ };
1360
+
1361
+ const approaches = ['code-sandbox', 'decomposition', 'analogy'];
1362
+ expect(approaches.length).toBe(3);
1363
+ expect(approaches[0]).not.toBe(approaches[1]);
1364
+ expect(approaches[1]).not.toBe(approaches[2]);
1365
+ });
1366
+
1367
+ it('knowledge-extraction uses chain-of-questions', () => {
1368
+ const approaches = ['chain-of-questions', 'decomposition', 'counter-factual'];
1369
+ expect(approaches[0]).toBe('chain-of-questions');
1370
+ });
1371
+ });
1372
+
1373
+ describe('Budget optimization via marginal ROI', () => {
1374
+ it('estimates high ROI for improving experts', () => {
1375
+ function estimateMarginalROI(history: Array<{ score: number; iteration: number; cost: number }>, costPerIteration: number): number {
1376
+ if (history.length < 2) return 1.0;
1377
+ const recentWindow = Math.min(5, history.length);
1378
+ const recent = history.slice(-recentWindow);
1379
+ let totalImprovement = 0;
1380
+ let improvementCount = 0;
1381
+ for (let i = 1; i < recent.length; i++) {
1382
+ const delta = recent[i].score - recent[i - 1].score;
1383
+ if (delta > 0) {
1384
+ totalImprovement += delta;
1385
+ improvementCount++;
1386
+ }
1387
+ }
1388
+ const avgImprovement = improvementCount > 0 ? totalImprovement / improvementCount : 0;
1389
+ const pImprove = improvementCount / (recent.length - 1);
1390
+ const expectedImprovement = pImprove * avgImprovement;
1391
+ return costPerIteration > 0 ? expectedImprovement / costPerIteration : expectedImprovement;
1392
+ }
1393
+
1394
+ // Expert improving steadily
1395
+ const improving = [
1396
+ { score: 0.2, iteration: 0, cost: 0.001 },
1397
+ { score: 0.5, iteration: 1, cost: 0.001 },
1398
+ { score: 0.8, iteration: 2, cost: 0.001 },
1399
+ { score: 1.0, iteration: 3, cost: 0.001 },
1400
+ ];
1401
+ const roi = estimateMarginalROI(improving, 0.001);
1402
+ expect(roi).toBeGreaterThan(0);
1403
+
1404
+ // Expert stuck at same score
1405
+ const stuck = [
1406
+ { score: 0.0, iteration: 0, cost: 0.001 },
1407
+ { score: 0.0, iteration: 1, cost: 0.001 },
1408
+ { score: 0.0, iteration: 2, cost: 0.001 },
1409
+ ];
1410
+ const stuckRoi = estimateMarginalROI(stuck, 0.001);
1411
+ expect(stuckRoi).toBe(0);
1412
+ });
1413
+
1414
+ it('reallocates budget to high-ROI experts', () => {
1415
+ function reallocateBudget(
1416
+ expertHistories: Map<number, Array<{ score: number; iteration: number; cost: number }>>,
1417
+ totalRemainingIterations: number,
1418
+ totalRemainingBudget: number
1419
+ ): Map<number, number> {
1420
+ const allocation = new Map<number, number>();
1421
+ if (expertHistories.size === 0) return allocation;
1422
+
1423
+ const rois = new Map<number, number>();
1424
+ for (const [expertId, history] of expertHistories) {
1425
+ const avgCost = history.length > 0
1426
+ ? history.reduce((s, r) => s + r.cost, 0) / history.length
1427
+ : 0.001;
1428
+ // Simplified ROI for testing
1429
+ const lastScore = history.length > 0 ? history[history.length - 1].score : 0;
1430
+ const firstScore = history.length > 0 ? history[0].score : 0;
1431
+ const roi = Math.max(lastScore - firstScore, 0.01);
1432
+ rois.set(expertId, roi);
1433
+ }
1434
+
1435
+ const sorted = [...rois.entries()].sort((a, b) => b[1] - a[1]);
1436
+ const totalROI = sorted.reduce((s, [, roi]) => s + Math.max(roi, 0.01), 0);
1437
+
1438
+ for (const [expertId, roi] of sorted) {
1439
+ const proportion = Math.max(roi, 0.01) / totalROI;
1440
+ const iters = Math.max(1, Math.round(proportion * totalRemainingIterations));
1441
+ allocation.set(expertId, iters);
1442
+ }
1443
+
1444
+ for (const [expertId] of expertHistories) {
1445
+ if (!allocation.has(expertId)) allocation.set(expertId, 1);
1446
+ }
1447
+
1448
+ return allocation;
1449
+ }
1450
+
1451
+ const histories = new Map<number, Array<{ score: number; iteration: number; cost: number }>>();
1452
+ histories.set(0, [
1453
+ { score: 0.2, iteration: 0, cost: 0.001 },
1454
+ { score: 0.5, iteration: 1, cost: 0.001 },
1455
+ ]);
1456
+ histories.set(1, [
1457
+ { score: 0.0, iteration: 0, cost: 0.001 },
1458
+ { score: 0.0, iteration: 1, cost: 0.001 },
1459
+ ]);
1460
+
1461
+ const allocation = reallocateBudget(histories, 10, 1);
1462
+ expect(allocation.get(0)).toBeGreaterThan(allocation.get(1)!);
1463
+ });
1464
+ });
1465
+
1466
+ describe('Cross-domain transfer', () => {
1467
+ it('finds analogous categories', () => {
1468
+ const CATEGORY_ANALOGIES: Record<string, string[]> = {
1469
+ 'grid-transformation': ['pattern-completion', 'spatial-reasoning', 'sequence-prediction'],
1470
+ 'pattern-completion': ['grid-transformation', 'sequence-prediction'],
1471
+ 'knowledge-synthesis': ['logical-inference', 'mathematical'],
1472
+ };
1473
+
1474
+ expect(CATEGORY_ANALOGIES['grid-transformation']).toContain('pattern-completion');
1475
+ expect(CATEGORY_ANALOGIES['pattern-completion']).toContain('grid-transformation');
1476
+ expect(CATEGORY_ANALOGIES['knowledge-synthesis']).toContain('logical-inference');
1477
+ });
1478
+
1479
+ it('category descriptions are comprehensive', () => {
1480
+ const descs: Record<string, string> = {
1481
+ 'grid-transformation': '2D array transformations',
1482
+ 'pattern-completion': 'Completing partial patterns',
1483
+ 'knowledge-synthesis': 'Synthesizing fragmented knowledge',
1484
+ };
1485
+ expect(Object.keys(descs).length).toBeGreaterThanOrEqual(3);
1486
+ for (const desc of Object.values(descs)) {
1487
+ expect(desc.length).toBeGreaterThan(5);
1488
+ }
1489
+ });
1490
+ });
1491
+
1492
+ describe('Confidence-weighted voting', () => {
1493
+ it('weights passed solutions by iteration efficiency', () => {
1494
+ // A solution that passes in 1 iteration should rank higher than one that passes in 5
1495
+ // even if they produce the same output
1496
+ const confidenceWeight = (res: { score: number; iteration: number; passed: boolean; trainResults: Array<{ softScore: number }> }) => {
1497
+ let weight = res.score;
1498
+ if (res.passed) {
1499
+ weight *= Math.max(0.5, 1 - res.iteration * 0.05);
1500
+ }
1501
+ const avgSoft = res.trainResults.length > 0
1502
+ ? res.trainResults.reduce((s, r) => s + r.softScore, 0) / res.trainResults.length
1503
+ : 0;
1504
+ if (avgSoft > 0.8) weight *= 1.2;
1505
+ return weight;
1506
+ };
1507
+
1508
+ const fastSolution = { score: 1.0, iteration: 0, passed: true, trainResults: [{ softScore: 1.0 }] };
1509
+ const slowSolution = { score: 1.0, iteration: 5, passed: true, trainResults: [{ softScore: 1.0 }] };
1510
+
1511
+ const fastWeight = confidenceWeight(fastSolution);
1512
+ const slowWeight = confidenceWeight(slowSolution);
1513
+ expect(fastWeight).toBeGreaterThan(slowWeight);
1514
+ });
1515
+
1516
+ it('boosts high soft-score solutions', () => {
1517
+ const confidenceWeight = (res: { score: number; iteration: number; passed: boolean; trainResults: Array<{ softScore: number }> }) => {
1518
+ let weight = res.score;
1519
+ const avgSoft = res.trainResults.length > 0
1520
+ ? res.trainResults.reduce((s, r) => s + r.softScore, 0) / res.trainResults.length
1521
+ : 0;
1522
+ if (avgSoft > 0.8) weight *= 1.2;
1523
+ return weight;
1524
+ };
1525
+
1526
+ const highSoft = { score: 0.9, iteration: 0, passed: false, trainResults: [{ softScore: 0.9 }] };
1527
+ const lowSoft = { score: 0.9, iteration: 0, passed: false, trainResults: [{ softScore: 0.3 }] };
1528
+
1529
+ expect(confidenceWeight(highSoft)).toBeGreaterThan(confidenceWeight(lowSoft));
1530
+ });
1531
+ });
1532
+
1533
+ describe('Progressive difficulty', () => {
1534
+ it('orders training examples from easiest to hardest', () => {
1535
+ function flatten(arr: unknown[]): number[] {
1536
+ const result: number[] = [];
1537
+ const stack: unknown[] = [arr];
1538
+ while (stack.length > 0) {
1539
+ const item = stack.pop()!;
1540
+ if (Array.isArray(item)) {
1541
+ for (let i = item.length - 1; i >= 0; i--) stack.push(item[i]);
1542
+ } else if (typeof item === 'number') {
1543
+ result.push(item);
1544
+ }
1545
+ }
1546
+ return result;
1547
+ }
1548
+
1549
+ function gridSize(arr: unknown[]): number {
1550
+ return flatten(arr).length;
1551
+ }
1552
+
1553
+ function orderByDifficulty(trainInputs: unknown[], trainOutputs: unknown[]): number[] {
1554
+ const indices = trainInputs.map((_, i) => i);
1555
+ const difficulty = (input: unknown, output: unknown): number => {
1556
+ const inArr = Array.isArray(input) ? input : [];
1557
+ const outArr = Array.isArray(output) ? output : [];
1558
+ const inSize = gridSize(inArr);
1559
+ const outSize = gridSize(outArr);
1560
+ const sizeScore = Math.max(inSize, outSize);
1561
+ const uniqueVals = new Set(flatten(outArr)).size;
1562
+ const asymmetry = Math.abs(inSize - outSize);
1563
+ return sizeScore + uniqueVals * 2 + asymmetry * 3;
1564
+ };
1565
+ const scored = indices.map(i => ({ index: i, diff: difficulty(trainInputs[i], trainOutputs[i]) }));
1566
+ scored.sort((a, b) => a.diff - b.diff);
1567
+ return scored.map(s => s.index);
1568
+ }
1569
+
1570
+ // Small grid should come before large grid
1571
+ const trainInputs = [
1572
+ [[1, 2], [3, 4]],
1573
+ [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
1574
+ ];
1575
+ const trainOutputs = [
1576
+ [[5, 6], [7, 8]],
1577
+ [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
1578
+ ];
1579
+
1580
+ const order = orderByDifficulty(trainInputs, trainOutputs);
1581
+ expect(order[0]).toBe(0); // 2x2 grid before 3x3
1582
+ });
1583
+
1584
+ it('simplest example is first', () => {
1585
+ function flatten(arr: unknown[]): number[] {
1586
+ const result: number[] = [];
1587
+ const stack: unknown[] = [arr];
1588
+ while (stack.length > 0) {
1589
+ const item = stack.pop()!;
1590
+ if (Array.isArray(item)) {
1591
+ for (let i = item.length - 1; i >= 0; i--) stack.push(item[i]);
1592
+ } else if (typeof item === 'number') {
1593
+ result.push(item);
1594
+ }
1595
+ }
1596
+ return result;
1597
+ }
1598
+ function gridSize(arr: unknown[]): number { return flatten(arr).length; }
1599
+
1600
+ function orderByDifficulty(trainInputs: unknown[], trainOutputs: unknown[]): number[] {
1601
+ const indices = trainInputs.map((_, i) => i);
1602
+ const difficulty = (input: unknown, output: unknown): number => {
1603
+ const inArr = Array.isArray(input) ? input : [];
1604
+ const outArr = Array.isArray(output) ? output : [];
1605
+ const inSize = gridSize(inArr);
1606
+ const outSize = gridSize(outArr);
1607
+ return Math.max(inSize, outSize) + new Set(flatten(outArr)).size * 2;
1608
+ };
1609
+ const scored = indices.map(i => ({ index: i, diff: difficulty(trainInputs[i], trainOutputs[i]) }));
1610
+ scored.sort((a, b) => a.diff - b.diff);
1611
+ return scored.map(s => s.index);
1612
+ }
1613
+
1614
+ // 3 examples of increasing complexity
1615
+ const trainInputs = [
1616
+ [[1]],
1617
+ [[1, 2], [3, 4]],
1618
+ [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
1619
+ ];
1620
+ const trainOutputs = [
1621
+ [[1]],
1622
+ [[1, 2], [3, 4]],
1623
+ [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
1624
+ ];
1625
+
1626
+ const order = orderByDifficulty(trainInputs, trainOutputs);
1627
+ expect(order).toEqual([0, 1, 2]);
1628
+ });
1629
+ });
1630
+
1631
+ describe('Decomposition', () => {
1632
+ it('produces sub-problems with valid structure', () => {
1633
+ const subProblem = {
1634
+ id: 1,
1635
+ description: 'Identify the rotation angle',
1636
+ input: '[[1,2],[3,4]]',
1637
+ expectedOutput: '[[3,1],[4,2]]',
1638
+ combineOrder: 1,
1639
+ };
1640
+ expect(subProblem.id).toBe(1);
1641
+ expect(subProblem.description.length).toBeGreaterThan(0);
1642
+ expect(subProblem.input.length).toBeGreaterThan(0);
1643
+ });
1644
+
1645
+ it('combine strategies are valid', () => {
1646
+ const strategies = ['sequential', 'parallel', 'hierarchical'] as const;
1647
+ expect(strategies.length).toBe(3);
1648
+ });
1649
+ });
1650
+
1651
+ describe('Layer 14: Per-problem prompt synthesis', () => {
1652
+ it('computes problem fingerprints by structural features', () => {
1653
+ function flatten(arr: unknown[]): number[] {
1654
+ const result: number[] = [];
1655
+ const stack: unknown[] = [arr];
1656
+ while (stack.length > 0) {
1657
+ const item = stack.pop()!;
1658
+ if (Array.isArray(item)) {
1659
+ for (let i = item.length - 1; i >= 0; i--) stack.push(item[i]);
1660
+ } else if (typeof item === 'number') {
1661
+ result.push(item);
1662
+ }
1663
+ }
1664
+ return result;
1665
+ }
1666
+
1667
+ function problemFingerprint(problem: string, trainInputs: unknown[], trainOutputs: unknown[]): string {
1668
+ const features: string[] = [];
1669
+ if (trainInputs.length > 0) {
1670
+ const input = trainInputs[0];
1671
+ if (Array.isArray(input)) {
1672
+ const flat = flatten(input);
1673
+ features.push(`grid:${flat.length}`);
1674
+ features.push(`unique:${new Set(flat).size}`);
1675
+ }
1676
+ }
1677
+ const lower = problem.toLowerCase();
1678
+ if (lower.includes('rotate')) features.push('op:rotate');
1679
+ if (lower.includes('count')) features.push('op:count');
1680
+ if (lower.includes('fill')) features.push('op:fill');
1681
+ // Size class
1682
+ if (trainInputs.length > 0 && Array.isArray(trainInputs[0])) {
1683
+ const size = flatten(trainInputs[0]);
1684
+ features.push(size.length <= 4 ? 'size:tiny' : size.length <= 16 ? 'size:small' : 'size:medium');
1685
+ }
1686
+ return features.join('|');
1687
+ }
1688
+
1689
+ // Grid problem with rotation
1690
+ const fp1 = problemFingerprint(
1691
+ 'Rotate the grid 90 degrees clockwise',
1692
+ [[[1,2],[3,4]]],
1693
+ [[[3,1],[4,2]]]
1694
+ );
1695
+ expect(fp1).toContain('grid:4');
1696
+ expect(fp1).toContain('op:rotate');
1697
+ expect(fp1).toContain('size:tiny');
1698
+
1699
+ // Counting problem
1700
+ const fp2 = problemFingerprint(
1701
+ 'Count the number of connected components',
1702
+ [[[1]]],
1703
+ [[[1]]]
1704
+ );
1705
+ expect(fp2).toContain('op:count');
1706
+ expect(fp2).toContain('size:tiny');
1707
+
1708
+ // Different problems of the same type should have similar fingerprints
1709
+ const fp3 = problemFingerprint(
1710
+ 'Rotate this grid 90 degrees',
1711
+ [[[1,2,3],[4,5,6],[7,8,9]]],
1712
+ [[[7,4,1],[8,5,2],[9,6,3]]]
1713
+ );
1714
+ expect(fp3).toContain('op:rotate');
1715
+ });
1716
+
1717
+ it('SynthesizedPrompt structure is valid', () => {
1718
+ const synth = {
1719
+ id: 'test1',
1720
+ category: 'grid-transformation',
1721
+ problemFingerprint: 'grid:4|unique:4|op:rotate|size:small',
1722
+ solverPrompt: 'Specialized rotate solver $$problem$$',
1723
+ feedbackPrompt: 'Feedback $$feedback$$',
1724
+ configOverrides: { temperature: 0.8 },
1725
+ validationScore: 0.8,
1726
+ validationTests: 2,
1727
+ validated: true,
1728
+ created: Date.now(),
1729
+ useCount: 0,
1730
+ successCount: 0,
1731
+ avgScore: 0,
1732
+ };
1733
+ expect(synth.solverPrompt).toContain('$$problem$$');
1734
+ expect(synth.problemFingerprint).toContain('op:rotate');
1735
+ expect(synth.validated).toBe(true);
1736
+ });
1737
+ });
1738
+
1739
+ describe('Layer 15: Meta-meta level', () => {
1740
+ it('MetaHarness structure is valid', () => {
1741
+ const mh = {
1742
+ id: 'mh1',
1743
+ name: 'pattern-code-hybrid',
1744
+ description: 'Combines pattern recognition with code execution',
1745
+ solverPrompt: 'Analyze the pattern first, then write code $$problem$$',
1746
+ configOverrides: { temperature: 0.9 },
1747
+ rationale: 'Code-only approaches miss spatial patterns; pattern-only lacks precision',
1748
+ parentId: null,
1749
+ generation: 1,
1750
+ created: Date.now(),
1751
+ useCount: 0,
1752
+ successCount: 0,
1753
+ avgScore: 0,
1754
+ };
1755
+ expect(mh.solverPrompt).toContain('$$problem$$');
1756
+ expect(mh.generation).toBe(1);
1757
+ expect(mh.rationale.length).toBeGreaterThan(10);
1758
+ });
1759
+
1760
+ it('child meta-harness has incremented generation', () => {
1761
+ const parent = { id: 'mh1', generation: 1 };
1762
+ const child = {
1763
+ id: 'mh2',
1764
+ parentId: 'mh1',
1765
+ generation: parent.generation + 1,
1766
+ };
1767
+ expect(child.generation).toBe(2);
1768
+ expect(child.parentId).toBe('mh1');
1769
+ });
1770
+ });
1771
+
1772
+ describe('Layer 16: Gradient-based budget optimization', () => {
1773
+ it('estimates positive gradient for improving trajectories', () => {
1774
+ function estimateImprovementGradient(history: Array<{ score: number; iteration: number }>) {
1775
+ if (history.length < 2) return { gradient: 0, acceleration: 0, expectedNextScore: 0, confidence: 0 };
1776
+ const window = Math.min(5, history.length);
1777
+ const recent = history.slice(-window);
1778
+ let gradientSum = 0, gradientCount = 0;
1779
+ for (let i = 1; i < recent.length; i++) {
1780
+ const ds = recent[i].score - recent[i - 1].score;
1781
+ const di = recent[i].iteration - recent[i - 1].iteration;
1782
+ if (di > 0) { gradientSum += ds / di; gradientCount++; }
1783
+ }
1784
+ const gradient = gradientCount > 0 ? gradientSum / gradientCount : 0;
1785
+ const gradients: number[] = [];
1786
+ for (let i = 1; i < recent.length; i++) {
1787
+ gradients.push(recent[i].score - recent[i - 1].score);
1788
+ }
1789
+ let accelSum = 0, accelCount = 0;
1790
+ for (let i = 1; i < gradients.length; i++) {
1791
+ accelSum += gradients[i] - gradients[i - 1];
1792
+ accelCount++;
1793
+ }
1794
+ const acceleration = accelCount > 0 ? accelSum / accelCount : 0;
1795
+ const currentScore = history[history.length - 1].score;
1796
+ const expectedNextScore = Math.max(0, Math.min(1, currentScore + gradient + 0.5 * acceleration));
1797
+ const confidence = Math.min(1, recent.length / 5);
1798
+ return { gradient, acceleration, expectedNextScore, confidence };
1799
+ }
1800
+
1801
+ // Improving trajectory
1802
+ const improving = [
1803
+ { score: 0.0, iteration: 0 },
1804
+ { score: 0.3, iteration: 1 },
1805
+ { score: 0.6, iteration: 2 },
1806
+ { score: 0.8, iteration: 3 },
1807
+ { score: 1.0, iteration: 4 },
1808
+ ];
1809
+ const g = estimateImprovementGradient(improving);
1810
+ expect(g.gradient).toBeGreaterThan(0);
1811
+ expect(g.confidence).toBe(1);
1812
+
1813
+ // Stuck trajectory
1814
+ const stuck = [
1815
+ { score: 0.0, iteration: 0 },
1816
+ { score: 0.0, iteration: 1 },
1817
+ { score: 0.0, iteration: 2 },
1818
+ { score: 0.0, iteration: 3 },
1819
+ { score: 0.0, iteration: 4 },
1820
+ ];
1821
+ const sg = estimateImprovementGradient(stuck);
1822
+ expect(sg.gradient).toBe(0);
1823
+
1824
+ // Decelerating trajectory (acceleration < 0)
1825
+ const decel = [
1826
+ { score: 0.0, iteration: 0 },
1827
+ { score: 0.5, iteration: 1 },
1828
+ { score: 0.7, iteration: 2 },
1829
+ { score: 0.75, iteration: 3 },
1830
+ { score: 0.78, iteration: 4 },
1831
+ ];
1832
+ const dg = estimateImprovementGradient(decel);
1833
+ expect(dg.acceleration).toBeLessThan(0); // slowing down
1834
+ });
1835
+
1836
+ it('gradient allocation favors improving experts', () => {
1837
+ function gradientBudgetAllocation(
1838
+ trajectories: Array<{ expertId: number; history: Array<{ score: number; iteration: number }> }>,
1839
+ totalRemainingIterations: number
1840
+ ): Map<number, number> {
1841
+ const allocation = new Map<number, number>();
1842
+ if (trajectories.length === 0) return allocation;
1843
+ const weights = trajectories.map(t => {
1844
+ if (t.history.length < 2) return 0.01;
1845
+ const recent = t.history.slice(-5);
1846
+ const lastScore = recent[recent.length - 1].score;
1847
+ const firstScore = recent[0].score;
1848
+ const improvement = Math.max(0, lastScore - firstScore);
1849
+ const confidence = Math.min(1, recent.length / 5);
1850
+ return improvement * confidence + 0.01;
1851
+ });
1852
+ const totalWeight = weights.reduce((s, w) => s + w, 0);
1853
+ for (const [i, t] of trajectories.entries()) {
1854
+ const proportion = weights[i] / totalWeight;
1855
+ const iters = Math.max(1, Math.round(proportion * totalRemainingIterations));
1856
+ allocation.set(t.expertId, iters);
1857
+ }
1858
+ return allocation;
1859
+ }
1860
+
1861
+ const improving = {
1862
+ expertId: 0,
1863
+ history: [
1864
+ { score: 0.2, iteration: 0 },
1865
+ { score: 0.5, iteration: 1 },
1866
+ { score: 0.8, iteration: 2 },
1867
+ ],
1868
+ };
1869
+ const stuck = {
1870
+ expertId: 1,
1871
+ history: [
1872
+ { score: 0.0, iteration: 0 },
1873
+ { score: 0.0, iteration: 1 },
1874
+ { score: 0.0, iteration: 2 },
1875
+ ],
1876
+ };
1877
+
1878
+ const alloc = gradientBudgetAllocation([improving, stuck], 10);
1879
+ expect(alloc.get(0)).toBeGreaterThan(alloc.get(1)!);
1880
+ });
1881
+
1882
+ it('shouldSwitchApproach detects stuck experts', () => {
1883
+ function shouldSwitchApproach(history: Array<{ score: number; iteration: number }>): boolean {
1884
+ if (history.length < 3) return false;
1885
+ // Check if gradient ≈ 0 and decelerating
1886
+ const recent = history.slice(-5);
1887
+ let gradientSum = 0, gradientCount = 0;
1888
+ for (let i = 1; i < recent.length; i++) {
1889
+ const ds = recent[i].score - recent[i - 1].score;
1890
+ gradientSum += ds;
1891
+ gradientCount++;
1892
+ }
1893
+ const gradient = gradientCount > 0 ? gradientSum / gradientCount : 0;
1894
+ const gradients: number[] = [];
1895
+ for (let i = 1; i < recent.length; i++) {
1896
+ gradients.push(recent[i].score - recent[i - 1].score);
1897
+ }
1898
+ let accelSum = 0, accelCount = 0;
1899
+ for (let i = 1; i < gradients.length; i++) {
1900
+ accelSum += gradients[i] - gradients[i - 1];
1901
+ accelCount++;
1902
+ }
1903
+ const acceleration = accelCount > 0 ? accelSum / accelCount : 0;
1904
+ const confidence = Math.min(1, recent.length / 5);
1905
+ return confidence > 0.6 && gradient < 0.01 && acceleration < -0.01;
1906
+ }
1907
+
1908
+ // Stuck expert
1909
+ const stuck = [
1910
+ { score: 0.0, iteration: 0 },
1911
+ { score: 0.0, iteration: 1 },
1912
+ { score: 0.0, iteration: 2 },
1913
+ { score: 0.0, iteration: 3 },
1914
+ ];
1915
+ // Not stuck (zero gradient but no negative acceleration)
1916
+ expect(shouldSwitchApproach(stuck)).toBe(false); // gradient=0, acceleration=0, not < -0.01
1917
+
1918
+ // Decelerating expert (stuck then declining)
1919
+ const decel = [
1920
+ { score: 0.6, iteration: 0 },
1921
+ { score: 0.6, iteration: 1 },
1922
+ { score: 0.6, iteration: 2 },
1923
+ { score: 0.6, iteration: 3 },
1924
+ { score: 0.55, iteration: 4 },
1925
+ ];
1926
+ expect(shouldSwitchApproach(decel)).toBe(true);
1927
+ });
1928
+ });
1929
+
1930
+ describe('Layer 17: Recursive meta-meta nesting', () => {
1931
+ it('selectMetaHarnessExpertConfig returns null when no meta-harnesses exist', () => {
1932
+ // With empty meta-harnesses array, should return null
1933
+ const baseConfig = {
1934
+ solverPrompt: 'Test $$problem$$',
1935
+ feedbackPrompt: 'Feedback $$feedback$$',
1936
+ temperature: 1.0,
1937
+ maxIterations: 10,
1938
+ } as any;
1939
+ // No meta-harnesses loaded → null
1940
+ expect(true).toBe(true); // placeholder for structural validation
1941
+ });
1942
+
1943
+ it('meta-harnesses can evolve recursively', () => {
1944
+ const parent = { id: 'mh1', generation: 1, useCount: 3, avgScore: 0.3 };
1945
+ const child = { id: 'mh2', parentId: 'mh1', generation: 2 };
1946
+ // Recursive: child can itself have a child
1947
+ const grandchild = { id: 'mh3', parentId: 'mh2', generation: 3 };
1948
+ expect(grandchild.generation).toBe(3);
1949
+ expect(grandchild.parentId).toBe('mh2');
1950
+ });
1951
+
1952
+ it('recursiveMetaEvolve only evolves underperforming meta-harnesses', () => {
1953
+ // Only evolve if useCount >= 2 AND avgScore < 0.5 AND generation < maxGenerations
1954
+ const eligible = [
1955
+ { useCount: 3, avgScore: 0.3, generation: 1 }, // eligible
1956
+ { useCount: 1, avgScore: 0.3, generation: 1 }, // too few uses
1957
+ { useCount: 5, avgScore: 0.8, generation: 1 }, // performing well
1958
+ { useCount: 5, avgScore: 0.3, generation: 3 }, // max generation
1959
+ ].filter(m => m.useCount >= 2 && m.avgScore < 0.5 && m.generation < 3);
1960
+ expect(eligible.length).toBe(1);
1961
+ });
1962
+ });
1963
+
1964
+ describe('Layer 18: Multi-model decomposition', () => {
1965
+ it('RoutedSubProblem structure is valid', () => {
1966
+ const sub: RoutedSubProblem = {
1967
+ id: 1,
1968
+ description: 'Identify rotation angle',
1969
+ model: 'anthropic/claude-sonnet-4-5',
1970
+ dependsOn: null,
1971
+ input: 'Grid: [[1,2],[3,4]]',
1972
+ };
1973
+ expect(sub.id).toBe(1);
1974
+ expect(sub.model).toContain('/');
1975
+ });
1976
+
1977
+ it('model strength heuristics cover common providers', () => {
1978
+ const strengths: Record<string, string[]> = {
1979
+ anthropic: ['complex reasoning', 'code generation', 'long-context analysis'],
1980
+ openai: ['general reasoning', 'math', 'code', 'creative tasks'],
1981
+ google: ['multimodal', 'long context', 'factual knowledge'],
1982
+ groq: ['fast inference', 'simple reasoning', 'classification'],
1983
+ wafer: ['reasoning with thinking', 'Chinese+English', 'code'],
1984
+ deepseek: ['code', 'math', 'reasoning'],
1985
+ };
1986
+ expect(Object.keys(strengths).length).toBeGreaterThanOrEqual(6);
1987
+ for (const [provider, caps] of Object.entries(strengths)) {
1988
+ expect(caps.length).toBeGreaterThanOrEqual(3);
1989
+ }
1990
+ });
1991
+
1992
+ it('decomposeAndRoute returns null for single model', () => {
1993
+ // With only 1 model, no routing benefit
1994
+ expect(true).toBe(true); // structural validation
1995
+ });
1996
+
1997
+ it('dependency ordering works correctly', () => {
1998
+ const subs: RoutedSubProblem[] = [
1999
+ { id: 1, description: 'Step 1', model: 'openai/gpt-4o', dependsOn: null, input: '' },
2000
+ { id: 2, description: 'Step 2', model: 'anthropic/claude-sonnet-4-5', dependsOn: 1, input: '' },
2001
+ { id: 3, description: 'Step 3', model: 'openai/gpt-4o', dependsOn: 2, input: '' },
2002
+ ];
2003
+ const sorted = [...subs].sort((a, b) => {
2004
+ if (a.dependsOn === null && b.dependsOn !== null) return -1;
2005
+ if (a.dependsOn !== null && b.dependsOn === null) return 1;
2006
+ return 0;
2007
+ });
2008
+ expect(sorted[0].id).toBe(1); // Independent task first
2009
+ });
2010
+ });
2011
+
2012
+ describe('Layer 19: Per-iteration prompt adaptation', () => {
2013
+ it('IterationAdaptation types are valid', () => {
2014
+ const adaptations: IterationAdaptation[] = [
2015
+ { type: 'pre-insert', content: 'Focus on spatial patterns', rationale: 'Grid problems benefit from spatial analysis' },
2016
+ { type: 'anti-pattern', content: 'Do not use nested loops for simple transforms', rationale: 'Performance issue' },
2017
+ { type: 'section-replace', content: 'Use Map/Set for lookups', section: 'Part 2', rationale: 'Better pattern matching' },
2018
+ ];
2019
+ expect(adaptations.length).toBe(3);
2020
+ for (const a of adaptations) {
2021
+ expect(['pre-insert', 'anti-pattern', 'section-replace']).toContain(a.type);
2022
+ expect(a.content.length).toBeGreaterThan(0);
2023
+ }
2024
+ });
2025
+
2026
+ it('applyIterationAdaptation: pre-insert adds content before problem', () => {
2027
+ const prompt = 'Solve this: $$problem$$ Good luck!';
2028
+ const adaptation: IterationAdaptation = {
2029
+ type: 'pre-insert',
2030
+ content: 'Focus on rotation patterns.',
2031
+ rationale: 'test',
2032
+ };
2033
+ const result = applyIterationAdaptation(prompt, adaptation);
2034
+ expect(result).toContain('Focus on rotation patterns.');
2035
+ expect(result).toContain('$$problem$$');
2036
+ });
2037
+
2038
+ it('applyIterationAdaptation: anti-pattern adds warning after problem', () => {
2039
+ const prompt = 'Solve this: $$problem$$ Good luck!';
2040
+ const adaptation: IterationAdaptation = {
2041
+ type: 'anti-pattern',
2042
+ content: 'Avoid nested loops.',
2043
+ rationale: 'test',
2044
+ };
2045
+ const result = applyIterationAdaptation(prompt, adaptation);
2046
+ expect(result).toContain('Anti-pattern to avoid: Avoid nested loops.');
2047
+ });
2048
+
2049
+ it('adaptPromptMidSolve only triggers after 3 failed iterations', () => {
2050
+ // Less than 3 failures → no adaptation
2051
+ const fewFailures = [
2052
+ { score: 0.0, feedback: 'wrong', iteration: 0 },
2053
+ { score: 0.8, feedback: 'almost', iteration: 1 }, // Not a failure
2054
+ ];
2055
+ const recentFailures = fewFailures.filter(h => h.score < 0.5);
2056
+ expect(recentFailures.length).toBeLessThan(2); // Need >=2 recent failures
2057
+ });
2058
+ });
2059
+
2060
+ describe('Layer 20: ARC-AGI benchmark integration', () => {
2061
+ it('ArcChallenge structure is valid', () => {
2062
+ const challenge: ArcChallenge = {
2063
+ id: 'test-001',
2064
+ trainInputs: [[[1, 2], [3, 4]]],
2065
+ trainOutputs: [[[3, 1], [4, 2]]],
2066
+ testInputs: [[[5, 6], [7, 8]]],
2067
+ testOutputs: [[[7, 5], [8, 6]]],
2068
+ };
2069
+ expect(challenge.id).toBe('test-001');
2070
+ expect(challenge.trainInputs.length).toBe(1);
2071
+ expect(challenge.testOutputs).toBeDefined();
2072
+ });
2073
+
2074
+ it('loadArcChallenges handles missing files gracefully', () => {
2075
+ // Should return empty array for non-existent file
2076
+ const fs = require('fs');
2077
+ const exists = fs.existsSync('/tmp/nonexistent_arc.json');
2078
+ expect(exists).toBe(false);
2079
+ });
2080
+
2081
+ it('benchmark metrics are computed correctly', () => {
2082
+ const results = [
2083
+ { id: '001', passed: true, bestScore: 1.0, cost: 0.01, time: 10 },
2084
+ { id: '002', passed: false, bestScore: 0.5, cost: 0.02, time: 20 },
2085
+ { id: '003', passed: false, bestScore: 0.0, cost: 0.015, time: 15 },
2086
+ ];
2087
+ const total = results.length;
2088
+ const solved = results.filter(r => r.passed).length;
2089
+ const partialSolved = results.filter(r => r.bestScore > 0.5).length;
2090
+ const avgBestScore = results.reduce((s, r) => s + r.bestScore, 0) / total;
2091
+ expect(solved).toBe(1);
2092
+ expect(partialSolved).toBe(1); // only the solved one has score > 0.5
2093
+ expect(avgBestScore).toBeCloseTo(0.5, 5);
2094
+ });
2095
+ });