diff --git a/packages/imhotep-solver/src/logic-engine.test.ts b/packages/imhotep-solver/src/logic-engine.test.ts index 897e2fe..7071f48 100644 --- a/packages/imhotep-solver/src/logic-engine.test.ts +++ b/packages/imhotep-solver/src/logic-engine.test.ts @@ -361,7 +361,7 @@ describe('predicate calls', () => { assert.ok(!result.diagnostics.some((d) => d.code === 'IMH_FEATURE_NOT_YET_IMPLEMENTED')); }); - it('returns not-yet-implemented for aligned-with variants', () => { + it('evaluates aligned-with directional variants correctly', () => { const world = makeWorld(); const variants = ['leftAlignedWith', 'rightAlignedWith', 'topAlignedWith', 'bottomAlignedWith']; for (const variant of variants) { @@ -391,11 +391,12 @@ describe('predicate calls', () => { }; const result = evaluateLogic(input); - assert.strictEqual(result.passed, false, `Expected ${variant} to fail`); - assert.ok( - result.diagnostics.some((d) => d.code === 'IMH_FEATURE_NOT_YET_IMPLEMENTED'), - `Expected ${variant} to produce IMH_FEATURE_NOT_YET_IMPLEMENTED` - ); + // Elements 1 (0,0,100,40) and 2 (110,50,210,90) are not aligned + // on any side, so all directional alignments should produce determinate + // false results (not IMH_FEATURE_NOT_YET_IMPLEMENTED). + assert.strictEqual(result.passed, false, `Expected ${variant} to evaluate, not return NYI`); + assert.strictEqual(result.formulaResults.length, 1); + assert.strictEqual(result.formulaResults[0].outcome, 'fail'); } }); }); diff --git a/packages/imhotep-solver/src/predicates.ts b/packages/imhotep-solver/src/predicates.ts index 69aba27..4723591 100644 --- a/packages/imhotep-solver/src/predicates.ts +++ b/packages/imhotep-solver/src/predicates.ts @@ -972,10 +972,33 @@ export const separatedFromPredicate: PredicateEvaluator = { return makePredicateResult(pass ? 'true' : 'false', metrics, [subjectId, referenceId]); }, }; -export const leftAlignedWithPredicate = makeNotImplementedPredicate('leftAlignedWith'); -export const rightAlignedWithPredicate = makeNotImplementedPredicate('rightAlignedWith'); -export const topAlignedWithPredicate = makeNotImplementedPredicate('topAlignedWith'); -export const bottomAlignedWithPredicate = makeNotImplementedPredicate('bottomAlignedWith'); +export const leftAlignedWithPredicate: PredicateEvaluator = { + descriptor: { name: 'leftAlignedWith', arity: 2, domains: ['element', 'element'], requiredFacts: ['subject.primaryBox', 'reference.primaryBox'] }, + evaluateTuple(world, tuple, options) { + return alignedWithPredicate.evaluateTuple(world, tuple, { ...options as Record, axis: 'left' }); + }, +}; + +export const rightAlignedWithPredicate: PredicateEvaluator = { + descriptor: { name: 'rightAlignedWith', arity: 2, domains: ['element', 'element'], requiredFacts: ['subject.primaryBox', 'reference.primaryBox'] }, + evaluateTuple(world, tuple, options) { + return alignedWithPredicate.evaluateTuple(world, tuple, { ...options as Record, axis: 'right' }); + }, +}; + +export const topAlignedWithPredicate: PredicateEvaluator = { + descriptor: { name: 'topAlignedWith', arity: 2, domains: ['element', 'element'], requiredFacts: ['subject.primaryBox', 'reference.primaryBox'] }, + evaluateTuple(world, tuple, options) { + return alignedWithPredicate.evaluateTuple(world, tuple, { ...options as Record, axis: 'top' }); + }, +}; + +export const bottomAlignedWithPredicate: PredicateEvaluator = { + descriptor: { name: 'bottomAlignedWith', arity: 2, domains: ['element', 'element'], requiredFacts: ['subject.primaryBox', 'reference.primaryBox'] }, + evaluateTuple(world, tuple, options) { + return alignedWithPredicate.evaluateTuple(world, tuple, { ...options as Record, axis: 'bottom' }); + }, +}; // --------------------------------------------------------------------------- // Register Defaults