mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
- Added a new GenUI module for managing user interface surfaces, including creation, response handling, and state synchronization. - Implemented API endpoints for listing and responding to GenUI surfaces associated with runs and projects. - Introduced event types and payload helpers for GenUI surface events, enhancing the interaction model for headless operations. - Established a persistent state writer for GenUI surfaces, ensuring reliable data management and retrieval. - Enhanced the plugin system to support auto-derived OAuth prompts for required connectors, improving user experience during plugin application.
72 lines
3 KiB
TypeScript
72 lines
3 KiB
TypeScript
// `until` evaluator (spec §10.1 / §10.2). Closed vocabulary, intentionally
|
|
// not arbitrary JS — the parser refuses signals it does not know about and
|
|
// only accepts == != >= <= > < operators. The evaluator is short-circuit
|
|
// per OR clause and returns the first matching conjunction so the run
|
|
// audit can record "what convinced me to advance".
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
evaluateUntil,
|
|
isParseableUntil,
|
|
parseUntil,
|
|
UntilSyntaxError,
|
|
} from '../src/plugins/until.js';
|
|
|
|
describe('parseUntil', () => {
|
|
it('accepts the §10.1 reference expression', () => {
|
|
const expr = parseUntil('critique.score>=4 || iterations>=3');
|
|
expect(expr.any).toHaveLength(2);
|
|
expect(expr.any[0]?.[0]).toMatchObject({ signal: 'critique.score', op: '>=', value: 4 });
|
|
expect(expr.any[1]?.[0]).toMatchObject({ signal: 'iterations', op: '>=', value: 3 });
|
|
});
|
|
|
|
it('accepts boolean signals with == / !=', () => {
|
|
const expr = parseUntil('user.confirmed == true');
|
|
expect(expr.any[0]?.[0]).toMatchObject({ signal: 'user.confirmed', op: '==', value: true });
|
|
});
|
|
|
|
it('rejects unknown signals', () => {
|
|
expect(() => parseUntil('lol.idk >= 1')).toThrow(UntilSyntaxError);
|
|
});
|
|
|
|
it('rejects boolean signal with > operator', () => {
|
|
expect(() => parseUntil('preview.ok > true')).toThrow(UntilSyntaxError);
|
|
});
|
|
|
|
it('rejects empty expressions', () => {
|
|
expect(() => parseUntil('')).toThrow(UntilSyntaxError);
|
|
});
|
|
|
|
it('isParseableUntil returns false for syntactically broken input', () => {
|
|
expect(isParseableUntil('critique.score nope 4')).toBe(false);
|
|
expect(isParseableUntil('critique.score >= 4')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('evaluateUntil', () => {
|
|
it('converges on the canonical critique-or-iterations termination', () => {
|
|
const expr = parseUntil('critique.score>=4 || iterations>=3');
|
|
expect(evaluateUntil(expr, { 'critique.score': 4, iterations: 1 })).toMatchObject({ satisfied: true });
|
|
expect(evaluateUntil(expr, { 'critique.score': 2, iterations: 3 })).toMatchObject({ satisfied: true });
|
|
expect(evaluateUntil(expr, { 'critique.score': 2, iterations: 1 })).toMatchObject({ satisfied: false });
|
|
});
|
|
|
|
it('boolean signals respect == / !=', () => {
|
|
const expr = parseUntil('user.confirmed == true');
|
|
expect(evaluateUntil(expr, { 'user.confirmed': true }).satisfied).toBe(true);
|
|
expect(evaluateUntil(expr, { 'user.confirmed': false }).satisfied).toBe(false);
|
|
});
|
|
|
|
it('missing signal value never satisfies a comparison', () => {
|
|
const expr = parseUntil('iterations>=3');
|
|
expect(evaluateUntil(expr, {}).satisfied).toBe(false);
|
|
});
|
|
|
|
it('returns the matching conjunction terms for audit', () => {
|
|
const expr = parseUntil('critique.score>=4 || iterations>=3');
|
|
const result = evaluateUntil(expr, { 'critique.score': 4, iterations: 0 });
|
|
expect(result.satisfied).toBe(true);
|
|
expect(result.matched).toHaveLength(1);
|
|
expect(result.matched[0]?.signal).toBe('critique.score');
|
|
});
|
|
});
|