98 lines
4.0 KiB
TypeScript
98 lines
4.0 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { MindDB } from '@waggle/core';
|
|
import { Orchestrator } from '../src/orchestrator.js';
|
|
import { FEATURE_FLAGS, isEnabled } from '../src/feature-flags.js';
|
|
import { MockEmbedder } from '../../hive-mind-core/tests/mind/helpers/mock-embedder.js';
|
|
|
|
/**
|
|
* Brief §12.4 — feature-flag snapshot tests. Proves:
|
|
* - PROMPT_ASSEMBLER flag exists and follows env-var convention.
|
|
* - W4 flip (2026-06-11): flag is DEFAULT ON; WAGGLE_PROMPT_ASSEMBLER=0 is
|
|
* the kill switch (vitest.setup.ts pins tests to 0 — legacy-path suites
|
|
* stay deterministic; assembler suites opt in explicitly).
|
|
* - Flag-off buildSystemPrompt path is stable — regression guard.
|
|
* - Flag-on path (buildAssembledPrompt) returns well-formed AssembledPrompt.
|
|
*/
|
|
describe('PROMPT_ASSEMBLER feature flag', () => {
|
|
it('flag key exists on FEATURE_FLAGS', () => {
|
|
expect('PROMPT_ASSEMBLER' in FEATURE_FLAGS).toBe(true);
|
|
});
|
|
|
|
it('flag is default-ON with kill switch WAGGLE_PROMPT_ASSEMBLER=0', () => {
|
|
const envValue = process.env['WAGGLE_PROMPT_ASSEMBLER'];
|
|
const expectedFlag = envValue !== '0';
|
|
expect(FEATURE_FLAGS.PROMPT_ASSEMBLER).toBe(expectedFlag);
|
|
expect(isEnabled('PROMPT_ASSEMBLER')).toBe(expectedFlag);
|
|
});
|
|
|
|
it('test environment pins the flag OFF (vitest.setup.ts invariant)', () => {
|
|
// vitest.setup.ts sets WAGGLE_PROMPT_ASSEMBLER=0 unless already set —
|
|
// the broad suites exercise the legacy path; eval harness conditions
|
|
// C/D/F override with =1 explicitly.
|
|
if (process.env['WAGGLE_PROMPT_ASSEMBLER'] === '0') {
|
|
expect(FEATURE_FLAGS.PROMPT_ASSEMBLER).toBe(false);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Orchestrator prompt paths — flag-off regression protection', () => {
|
|
let db: MindDB;
|
|
let orchestrator: Orchestrator;
|
|
|
|
beforeEach(() => {
|
|
db = new MindDB(':memory:');
|
|
orchestrator = new Orchestrator({
|
|
db,
|
|
embedder: new MockEmbedder(),
|
|
model: 'claude-opus-4-7',
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
db.close();
|
|
});
|
|
|
|
it('buildSystemPrompt() is stable across successive calls on a fresh mind', () => {
|
|
const a = orchestrator.buildSystemPrompt();
|
|
const b = orchestrator.buildSystemPrompt();
|
|
expect(a).toBe(b);
|
|
});
|
|
|
|
it('buildSystemPrompt() does not mention PromptAssembler sections by default', () => {
|
|
const out = orchestrator.buildSystemPrompt();
|
|
expect(out).not.toContain('# Response format');
|
|
expect(out).not.toContain('# Recent changes');
|
|
});
|
|
|
|
it('buildAssembledPrompt() returns well-formed output on a fresh mind', async () => {
|
|
const assembled = await orchestrator.buildAssembledPrompt('test query', null);
|
|
expect(typeof assembled.system).toBe('string');
|
|
expect(assembled.system.length).toBeGreaterThan(0);
|
|
expect(assembled.userPrefix).toBe('');
|
|
expect(assembled.debug.tier).toBe('frontier'); // opus-4-7 → frontier
|
|
expect(assembled.debug.totalChars).toBe(assembled.system.length);
|
|
expect(Array.isArray(assembled.debug.sectionsIncluded)).toBe(true);
|
|
});
|
|
|
|
it('buildAssembledPrompt() maps tier correctly for small models', async () => {
|
|
const smallDb = new MindDB(':memory:');
|
|
const smallOrch = new Orchestrator({
|
|
db: smallDb,
|
|
embedder: new MockEmbedder(),
|
|
model: 'gemma-4-31b',
|
|
});
|
|
const assembled = await smallOrch.buildAssembledPrompt('plan me a migration from A to B', null);
|
|
expect(assembled.debug.tier).toBe('small');
|
|
smallDb.close();
|
|
});
|
|
|
|
it('buildSystemPrompt() and buildAssembledPrompt() produce different shapes (flag-off vs PA path)', async () => {
|
|
const legacy = orchestrator.buildSystemPrompt();
|
|
const assembled = await orchestrator.buildAssembledPrompt('compare MECE vs BPMN', null);
|
|
// Both contain identity scaffolding, but the PA output has structured section headers
|
|
// the legacy path lacks (e.g., "## Persona:" when persona=null, "# State" via frame selection).
|
|
// The raw strings won't be equal.
|
|
expect(assembled.system).not.toBe(legacy);
|
|
});
|
|
});
|