import { WorkflowExecutor } from './src/vl/workflow-executor.js'; let passed = 0; let failed = 0; function test(name, fn) { return Promise.resolve() .then(fn) .then(() => { console.log(` ✓ ${name}`); passed++; }) .catch((err) => { console.log(` ✗ ${name}: ${err.message}`); failed++; }); } function assert(cond, msg) { if (!cond) throw new Error(msg || 'Assertion failed'); } console.log('\n── Workflow JSON Guard Regression ──'); function createExecutorWithResponse(text) { const executor = new WorkflowExecutor({ workDir: '/tmp/vlcode-json-guard', model: 'claude-opus-4-6' }); executor.client = { messages: { async create() { return { content: [{ type: 'text', text }], stop_reason: 'end_turn', usage: {}, model: 'stub-model', }; }, }, }; return executor; } await test('critical PRD generation rejects empty JSON output', async () => { const executor = createExecutorWithResponse('{}'); let threw = false; try { await executor._buildLLMAdapter().call({ docs: ['31'], output_config: { format: { type: 'json_object' } }, messages: [{ role: 'user', content: 'Requirement: test' }], }); } catch (err) { threw = true; assert(err.message.includes('Critical workflow JSON step'), `unexpected error: ${err.message}`); } assert(threw, 'expected critical JSON step to throw'); }); await test('non-critical JSON steps still fall back to empty object for compatibility', async () => { const executor = createExecutorWithResponse('No JSON here.'); const result = await executor._buildLLMAdapter().call({ docs: ['999'], output_config: { format: { type: 'json_object' } }, messages: [{ role: 'user', content: 'test' }], }); assert(result.content === '{}', `expected compatibility fallback, got ${result.content}`); }); await test('JSON repair removes superfluous closing braces from near-valid payloads', async () => { const malformed = '{"apps":[{"id":"Main"}],"sections":[{"id":"Home","internal":{"blocks":[]}}}],"components":[]}'; const executor = createExecutorWithResponse(malformed); const result = await executor._buildLLMAdapter().call({ docs: ['999'], output_config: { format: { type: 'json_object' } }, messages: [{ role: 'user', content: 'test' }], }); const parsed = JSON.parse(result.content); assert(Array.isArray(parsed.apps), 'apps should be preserved after repair'); assert(Array.isArray(parsed.sections), 'sections should be preserved after repair'); }); console.log(`\n── Results ──\n\n ${passed} passed, ${failed} failed\n`); process.exit(failed > 0 ? 1 : 0);