文件预览

solidifyLearning.test.js

查看 Evolver 技能包中的文件内容。

文件内容

test/solidifyLearning.test.js

const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const {
  classifyFailureMode,
  adaptGeneFromLearning,
  buildSoftFailureLearningSignals,
} = require('../src/gep/solidify');

describe('classifyFailureMode', () => {
  it('treats validation-only failures as soft and retryable', () => {
    const result = classifyFailureMode({
      constraintViolations: [],
      protocolViolations: [],
      validation: { ok: false, results: [{ ok: false, cmd: 'npm test' }] },
      canary: { ok: true, skipped: false },
    });
    assert.equal(result.mode, 'soft');
    assert.equal(result.reasonClass, 'validation');
    assert.equal(result.retryable, true);
  });

  it('treats destructive constraint failures as hard', () => {
    const result = classifyFailureMode({
      constraintViolations: ['CRITICAL_FILE_DELETED: MEMORY.md'],
      protocolViolations: [],
      validation: { ok: true, results: [] },
      canary: { ok: true, skipped: false },
    });
    assert.equal(result.mode, 'hard');
    assert.equal(result.reasonClass, 'constraint_destructive');
    assert.equal(result.retryable, false);
  });
});

describe('adaptGeneFromLearning', () => {
  it('adds structured success signals back into gene matching', () => {
    const gene = {
      type: 'Gene',
      id: 'gene_test',
      signals_match: ['error'],
    };
    adaptGeneFromLearning({
      gene,
      outcomeStatus: 'success',
      learningSignals: ['problem:performance', 'action:optimize', 'area:orchestration'],
      failureMode: { mode: 'none', reasonClass: null, retryable: false },
    });
    assert.ok(gene.signals_match.includes('problem:performance'));
    assert.ok(gene.signals_match.includes('area:orchestration'));
    assert.ok(!gene.signals_match.includes('action:optimize'));
    assert.ok(Array.isArray(gene.learning_history));
    assert.equal(gene.learning_history[0].outcome, 'success');
  });

  it('records failed anti-patterns without broadening matching', () => {
    const gene = {
      type: 'Gene',
      id: 'gene_test_fail',
      signals_match: ['protocol'],
    };
    adaptGeneFromLearning({
      gene,
      outcomeStatus: 'failed',
      learningSignals: ['problem:protocol', 'risk:validation'],
      failureMode: { mode: 'soft', reasonClass: 'validation', retryable: true },
    });
    assert.deepEqual(gene.signals_match, ['protocol']);
    assert.ok(Array.isArray(gene.anti_patterns));
    assert.equal(gene.anti_patterns[0].mode, 'soft');
  });
});

describe('buildSoftFailureLearningSignals', () => {
  it('extracts structured tags from validation failures', () => {
    const tags = buildSoftFailureLearningSignals({
      signals: ['perf_bottleneck'],
      failureReason: 'validation_failed: npm test => latency remained high',
      violations: [],
      validationResults: [
        { ok: false, cmd: 'npm test', stderr: 'latency remained high', stdout: '' },
      ],
    });
    assert.ok(tags.includes('problem:performance'));
    assert.ok(tags.includes('risk:validation'));
  });
});