87 lines
3.0 KiB
JavaScript
87 lines
3.0 KiB
JavaScript
|
|
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'));
|
||
|
|
});
|
||
|
|
});
|