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,196 @@
/**
* Conflict detection tests — post-Milestone-B trust slice.
*
* Tests the detectConflict heuristic and its integration with
* CombinedRetrieval and formatCombinedResult.
*/
import { describe, it, expect, vi } from 'vitest';
import {
detectConflict,
CombinedRetrieval,
type CombinedResult,
type MemorySearchLike,
type MemorySearchResultLike,
} from '../src/combined-retrieval.js';
import { formatCombinedResult } from '../src/tools.js';
import type { KvarkClientLike, KvarkSearchResponseLike } from '../src/kvark-tools.js';
// ── Helpers ──────────────────────────────────────────────────────────────
function makeResult(source: 'workspace' | 'kvark', content: string, score: number): CombinedResult {
return {
content,
source,
attribution: source === 'workspace' ? '[workspace memory]' : '[KVARK: doc]',
score,
metadata: {},
};
}
function makeMemoryResult(id: number, content: string, score: number): MemorySearchResultLike {
return { frame: { id, content, frame_type: 'fact', importance: 'normal' }, finalScore: score };
}
function makeSearch(results: MemorySearchResultLike[]): MemorySearchLike {
return { search: vi.fn().mockResolvedValue(results) };
}
function makeKvarkClient(results: Array<{ title: string; snippet: string; score: number }>): KvarkClientLike {
const response: KvarkSearchResponseLike = {
query: 'test',
total: results.length,
results: results.map((r, i) => ({
document_id: i + 1,
title: r.title,
snippet: r.snippet,
score: r.score,
document_type: null,
})),
};
return {
search: vi.fn().mockResolvedValue(response),
askDocument: vi.fn().mockResolvedValue({ answer: '', sources: [] }),
};
}
// ── detectConflict unit tests ────────────────────────────────────────────
describe('detectConflict', () => {
it('returns null when only workspace results are present', () => {
const ws = [makeResult('workspace', 'project approved by board', 0.9)];
expect(detectConflict(ws, [])).toBeNull();
});
it('returns null when only KVARK results are present', () => {
const kvark = [makeResult('kvark', 'project cancelled per policy', 0.8)];
expect(detectConflict([], kvark)).toBeNull();
});
it('returns null when both sources agree (both positive)', () => {
const ws = [makeResult('workspace', 'The migration was approved last week', 0.85)];
const kvark = [makeResult('kvark', 'Migration approved and confirmed by CTO', 0.9)];
expect(detectConflict(ws, kvark)).toBeNull();
});
it('returns null when both sources agree (both negative)', () => {
const ws = [makeResult('workspace', 'Feature was rejected in review', 0.8)];
const kvark = [makeResult('kvark', 'Feature rejected — not aligned with roadmap', 0.85)];
expect(detectConflict(ws, kvark)).toBeNull();
});
it('returns null when neither source has status language', () => {
const ws = [makeResult('workspace', 'The project uses PostgreSQL for data storage', 0.9)];
const kvark = [makeResult('kvark', 'Database architecture uses Oracle Enterprise', 0.85)];
expect(detectConflict(ws, kvark)).toBeNull();
});
it('flags conflict when workspace is positive and KVARK is negative', () => {
const ws = [makeResult('workspace', 'We decided to use the new API and approved the integration', 0.9)];
const kvark = [makeResult('kvark', 'API integration has been cancelled due to security review', 0.85)];
const result = detectConflict(ws, kvark);
expect(result).not.toBeNull();
expect(result).toContain('affirmative language');
expect(result).toContain('contradictory language');
expect(result).toContain('out of sync');
});
it('flags conflict when workspace is negative and KVARK is positive', () => {
const ws = [makeResult('workspace', 'The proposal was rejected by the committee', 0.8)];
const kvark = [makeResult('kvark', 'Proposal approved and budget confirmed for Q2', 0.9)];
const result = detectConflict(ws, kvark);
expect(result).not.toBeNull();
expect(result).toContain('enterprise source may be more current');
});
it('returns null when results are below score threshold', () => {
const ws = [makeResult('workspace', 'project approved', 0.4)];
const kvark = [makeResult('kvark', 'project cancelled', 0.5)];
expect(detectConflict(ws, kvark)).toBeNull();
});
it('handles mixed polarity within same source (neutral)', () => {
const ws = [makeResult('workspace', 'Some features approved, others rejected during review', 0.8)];
const kvark = [makeResult('kvark', 'Project cancelled after initial approval', 0.85)];
// workspace has both positive AND negative => neutral => no conflict
expect(detectConflict(ws, kvark)).toBeNull();
});
});
// ── Integration: CombinedRetrieval with conflict detection ──────────────
describe('CombinedRetrieval conflict integration', () => {
it('sets hasConflict=false when KVARK is not called', async () => {
const cr = new CombinedRetrieval({
workspaceSearch: makeSearch([makeMemoryResult(1, 'approved by team', 0.9)]),
personalSearch: makeSearch([]),
kvarkClient: null,
});
const result = await cr.search('test');
expect(result.hasConflict).toBe(false);
expect(result.conflictNote).toBeUndefined();
});
it('sets hasConflict=true when workspace and KVARK results conflict', async () => {
const cr = new CombinedRetrieval({
workspaceSearch: makeSearch([makeMemoryResult(1, 'The vendor was selected and approved', 0.85)]),
personalSearch: makeSearch([]),
kvarkClient: makeKvarkClient([
{ title: 'Vendor Policy', snippet: 'Vendor agreement has been cancelled effective immediately', score: 0.9 },
]),
});
const result = await cr.search('vendor status');
expect(result.hasConflict).toBe(true);
expect(result.conflictNote).toBeDefined();
expect(result.conflictNote).toContain('out of sync');
});
it('sets hasConflict=false when sources do not conflict', async () => {
const cr = new CombinedRetrieval({
workspaceSearch: makeSearch([makeMemoryResult(1, 'project status is active and healthy', 0.8)]),
personalSearch: makeSearch([]),
kvarkClient: makeKvarkClient([
{ title: 'Status', snippet: 'Project confirmed active with full funding', score: 0.85 },
]),
});
const result = await cr.search('project status');
expect(result.hasConflict).toBe(false);
});
});
// ── formatCombinedResult with conflict ──────────────────────────────────
describe('formatCombinedResult conflict rendering', () => {
it('includes Source Conflict section when hasConflict is true', () => {
const output = formatCombinedResult({
query: 'test',
workspaceResults: [makeResult('workspace', 'approved', 0.9)],
personalResults: [],
kvarkResults: [makeResult('kvark', 'cancelled', 0.85)],
kvarkAvailable: true,
kvarkSkipped: false,
hasConflict: true,
conflictNote: 'Sources may disagree.',
}, true);
expect(output).toContain('## Source Conflict');
expect(output).toContain('Sources may disagree.');
expect(output).toContain('Review both sources carefully');
});
it('omits Source Conflict section when hasConflict is false', () => {
const output = formatCombinedResult({
query: 'test',
workspaceResults: [makeResult('workspace', 'some fact', 0.9)],
personalResults: [],
kvarkResults: [makeResult('kvark', 'some other fact', 0.85)],
kvarkAvailable: true,
kvarkSkipped: false,
hasConflict: false,
}, true);
expect(output).not.toContain('## Source Conflict');
});
});