moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MindDB } from '../../src/mind/db.js';
import {
ImprovementSignalStore,
type SignalCategory,
} from '../../src/mind/improvement-signals.js';
describe('ImprovementSignalStore', () => {
let db: MindDB;
let store: ImprovementSignalStore;
beforeEach(() => {
db = new MindDB(':memory:');
store = new ImprovementSignalStore(db);
});
afterEach(() => {
db.close();
});
// ── record ─────────────────────────────────────────────────
describe('record', () => {
it('inserts a new signal on first record', () => {
const signal = store.record('capability_gap', 'missing:pdf_reader');
expect(signal.id).toBeDefined();
expect(signal.category).toBe('capability_gap');
expect(signal.pattern_key).toBe('missing:pdf_reader');
expect(signal.count).toBe(1);
expect(signal.surfaced).toBe(0);
});
it('increments count on duplicate (category, pattern_key)', () => {
store.record('correction', 'tone:too_formal');
store.record('correction', 'tone:too_formal');
const signal = store.record('correction', 'tone:too_formal');
expect(signal.count).toBe(3);
});
it('updates detail on upsert when new detail is non-empty', () => {
store.record('correction', 'format:headers', 'Use ## not ###');
const updated = store.record('correction', 'format:headers', 'Use h2 not h3');
expect(updated.detail).toBe('Use h2 not h3');
});
it('preserves existing detail when new detail is empty', () => {
store.record('correction', 'format:headers', 'Use ## not ###');
const updated = store.record('correction', 'format:headers');
expect(updated.detail).toBe('Use ## not ###');
});
it('stores metadata as JSON', () => {
const signal = store.record('workflow_pattern', 'shape:research', undefined, {
lastTask: 'analyze competitor',
avgSteps: 4,
});
const parsed = JSON.parse(signal.metadata);
expect(parsed.lastTask).toBe('analyze competitor');
expect(parsed.avgSteps).toBe(4);
});
it('keeps different pattern_keys separate within same category', () => {
store.record('capability_gap', 'missing:pdf_reader');
store.record('capability_gap', 'missing:web_search');
const gaps = store.getByCategory('capability_gap');
expect(gaps).toHaveLength(2);
});
});
// ── getByCategory ──────────────────────────────────────────
describe('getByCategory', () => {
it('returns only signals for the requested category', () => {
store.record('capability_gap', 'missing:pdf');
store.record('correction', 'tone:casual');
store.record('capability_gap', 'missing:search');
const gaps = store.getByCategory('capability_gap');
expect(gaps).toHaveLength(2);
expect(gaps.every(s => s.category === 'capability_gap')).toBe(true);
});
it('returns signals ordered by count descending', () => {
store.record('correction', 'a');
store.record('correction', 'b');
store.record('correction', 'b');
store.record('correction', 'b');
store.record('correction', 'a');
const corrections = store.getByCategory('correction');
expect(corrections[0].pattern_key).toBe('b');
expect(corrections[0].count).toBe(3);
expect(corrections[1].pattern_key).toBe('a');
expect(corrections[1].count).toBe(2);
});
it('returns empty array for category with no signals', () => {
expect(store.getByCategory('workflow_pattern')).toEqual([]);
});
});
// ── getActionable ──────────────────────────────────────────
describe('getActionable', () => {
it('returns signals above default thresholds', () => {
// capability_gap threshold = 2
store.record('capability_gap', 'missing:pdf');
store.record('capability_gap', 'missing:pdf');
const actionable = store.getActionable();
expect(actionable).toHaveLength(1);
expect(actionable[0].pattern_key).toBe('missing:pdf');
expect(actionable[0].parsedMetadata).toBeDefined();
});
it('excludes signals below threshold', () => {
// correction threshold = 3, only recorded twice
store.record('correction', 'tone:casual');
store.record('correction', 'tone:casual');
const actionable = store.getActionable();
expect(actionable).toHaveLength(0);
});
it('excludes already-surfaced signals', () => {
store.record('capability_gap', 'missing:pdf');
const signal = store.record('capability_gap', 'missing:pdf');
store.markSurfaced(signal.id);
const actionable = store.getActionable();
expect(actionable).toHaveLength(0);
});
it('caps at 3 results (MAX_ACTIONABLE)', () => {
for (let i = 0; i < 5; i++) {
const key = `gap_${i}`;
store.record('capability_gap', key);
store.record('capability_gap', key);
}
const actionable = store.getActionable();
expect(actionable.length).toBeLessThanOrEqual(3);
});
it('accepts custom thresholds', () => {
store.record('correction', 'tone:casual'); // count = 1
// Lower threshold to 1 — should now be actionable
const actionable = store.getActionable({ correction: 1 });
expect(actionable.some(s => s.pattern_key === 'tone:casual')).toBe(true);
});
it('includes parsedMetadata on results', () => {
store.record('capability_gap', 'missing:pdf', undefined, { tool: 'pdf_reader' });
store.record('capability_gap', 'missing:pdf');
const actionable = store.getActionable();
expect(actionable[0].parsedMetadata).toEqual({ tool: 'pdf_reader' });
});
});
// ── markSurfaced ───────────────────────────────────────────
describe('markSurfaced', () => {
it('sets surfaced=1 and surfaced_at', () => {
const signal = store.record('correction', 'tone:casual');
store.markSurfaced(signal.id);
const updated = store.get(signal.id);
expect(updated?.surfaced).toBe(1);
expect(updated?.surfaced_at).not.toBeNull();
});
});
// ── getByKey ───────────────────────────────────────────────
describe('getByKey', () => {
it('returns signal by category + pattern_key', () => {
store.record('workflow_pattern', 'shape:research');
const found = store.getByKey('workflow_pattern', 'shape:research');
expect(found).toBeDefined();
expect(found?.pattern_key).toBe('shape:research');
});
it('returns undefined for non-existent key', () => {
expect(store.getByKey('correction', 'nonexistent')).toBeUndefined();
});
});
// ── ensureTable (backward compat) ──────────────────────────
describe('ensureTable', () => {
it('creates table even on databases without it in schema', () => {
// The :memory: DB already has the table from schema.ts,
// but ensureTable should handle it gracefully
const store2 = new ImprovementSignalStore(db);
const signal = store2.record('capability_gap', 'test:compat');
expect(signal.count).toBe(1);
});
});
});