2ec6fdbe11
Add rule-based reflect() to aggregate task outcomes, environment-aware search scoring, experience_saved/recalled events, and structured injection blocks per the V1 schema. Co-authored-by: Cursor <cursoragent@cursor.com>
91 lines
3.6 KiB
JavaScript
91 lines
3.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
buildExecutionPatternAggregate,
|
|
environmentMatchBoost,
|
|
groupTaskOutcomesByProblem,
|
|
planExperienceReflection,
|
|
problemFingerprint,
|
|
} from './experience-reflect.mjs';
|
|
|
|
test('problemFingerprint normalizes punctuation and spacing', () => {
|
|
assert.equal(problemFingerprint(' SSE connection!!! '), 'sse connection');
|
|
});
|
|
|
|
test('groupTaskOutcomesByProblem groups active task outcomes by problem', () => {
|
|
const groups = groupTaskOutcomesByProblem([
|
|
{ id: '1', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'A' },
|
|
{ id: '2', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'B' },
|
|
{ id: '3', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'C' },
|
|
{ id: '4', kind: 'task_outcome', status: 'active', problem: 'Other', title: 'D' },
|
|
{ id: '5', kind: 'lesson', status: 'active', problem: 'SSE 断线', title: 'E' },
|
|
], 3);
|
|
assert.equal(groups.length, 1);
|
|
assert.equal(groups[0].length, 3);
|
|
});
|
|
|
|
test('environmentMatchBoost rewards component matches', () => {
|
|
const boost = environmentMatchBoost(
|
|
{ environment_json: JSON.stringify({ runtime: 'goosed', components: ['docker', 'postgres'] }) },
|
|
['goosed', 'docker'],
|
|
);
|
|
assert.equal(boost, 1);
|
|
});
|
|
|
|
test('buildExecutionPatternAggregate merges structured fields', () => {
|
|
const payload = buildExecutionPatternAggregate([
|
|
{
|
|
id: '1',
|
|
title: 'SSE 修复',
|
|
body: '改 server.rs',
|
|
problem: 'SSE connection instability',
|
|
result: 'success',
|
|
confidence: 0.8,
|
|
updated_at: 100,
|
|
environment_json: JSON.stringify({ runtime: 'goosed', components: ['docker'] }),
|
|
action_json: JSON.stringify({ executor: 'goose', steps: ['edit server.rs'] }),
|
|
evidence_json: JSON.stringify({ sources: [{ source_id: 'run:1' }] }),
|
|
},
|
|
{
|
|
id: '2',
|
|
title: 'SSE 再修',
|
|
body: '补 replay 映射',
|
|
problem: 'SSE connection instability',
|
|
result: 'success',
|
|
confidence: 0.9,
|
|
updated_at: 200,
|
|
environment_json: JSON.stringify({ components: ['portal'] }),
|
|
action_json: JSON.stringify({ executor: 'goose', steps: ['replay mapping'] }),
|
|
evidence_json: JSON.stringify({ sources: [{ source_id: 'run:2' }] }),
|
|
},
|
|
{
|
|
id: '3',
|
|
title: 'SSE 第三次',
|
|
body: '终态恢复',
|
|
problem: 'SSE connection instability',
|
|
result: 'partial',
|
|
confidence: 0.7,
|
|
updated_at: 300,
|
|
environment_json: JSON.stringify({ components: ['sse'] }),
|
|
action_json: JSON.stringify({ executor: 'goose', steps: ['finish sync'] }),
|
|
evidence_json: JSON.stringify({ sources: [{ source_id: 'run:1' }] }),
|
|
},
|
|
]);
|
|
assert.equal(payload.kind, 'execution_pattern');
|
|
assert.equal(payload.result, 'success');
|
|
assert.equal(payload.confidence, 0.8);
|
|
assert.deepEqual(payload.environment.components.sort(), ['docker', 'portal', 'sse']);
|
|
assert.equal(payload.evidence.sources.length, 2);
|
|
assert.deepEqual(payload.evidence.provenance.aggregated_from, ['3', '2', '1']);
|
|
});
|
|
|
|
test('planExperienceReflection skips groups with existing execution_pattern', () => {
|
|
const plans = planExperienceReflection([
|
|
{ id: '1', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'A' },
|
|
{ id: '2', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'B' },
|
|
{ id: '3', kind: 'task_outcome', status: 'active', problem: 'SSE 断线', title: 'C' },
|
|
{ id: 'p1', kind: 'execution_pattern', status: 'active', problem: 'SSE 断线', title: 'pattern' },
|
|
], { minGroupSize: 3 });
|
|
assert.equal(plans.length, 0);
|
|
});
|