Files
waggle-os/packages/agent/tests/prompt-assembler-feature-flag.test.ts
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

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);
});
});