Replace 21-item hardcoded isKeywordThatCanBePredicate array with
isPredicateName() from PredicateSpec (+ 'size' special case for
fluent API FOL bodies).
Replace 24-item parseRelation relationKinds array with
collectSpatialPredicateNames() from spec. Error message now also
auto-derives from the spec.
595 SDK + 57 E2E tests pass.
Replace 19-entry relationCodes string-to-integer map with
getPredicateRelationCode() from PredicateSpec. Keep only
aspectRatioBetween (15) as local DSL-keyword override.
Replace string-based unary detection ('atLeast'||'atMost') in
compileSimpleAssertionToFormula with isUnaryPredicate() from spec.
595 SDK + 57 E2E tests pass.
Replace 5 static Sets/maps (SPATIAL_RELATIONS, SIZE_RELATIONS,
VALID_OPTIONS, QUANTIFIER_COMPATIBLE, UNARY_RELATIONS) with
derivation from the unified PredicateSpec table:
- collectSpatialPredicateNames / collectSizePredicateNames
for category sets
- getPredicateValidOptions for option validation
- collectQuantifierCompatiblePredicateNames for quantifier checks
- isUnaryPredicate for unary detection
Local override map retained for fluent API dotted size variants
('size.atLeast' etc.) and 'aspectRatioBetween' DSL keyword, which
are input conventions, not distinct predicates.
595 SDK + 57 E2E tests pass.
compiler.ts: normalizeOptionValue now rejects NaN numbers and empty
strings. Previously typeof NaN === 'number' passed through and
propagated into option values, causing predicate comparisons like
value >= NaN to produce silent wrong results.
grammar.ts: parseAssertion now emits console.warn when silently
skipping unexpected tokens or failed clauses. Previously these
returns-null were invisible to developers.
454 solver+DSL + 57 E2E tests pass.
lower-to-canonical.ts: clauseEquivalent now compares compoundOperator
and compoundGroupId. Previously, compound assertions with different
operators (.and vs .or) were considered equivalent.
fol-compiler.ts: adaptGrammarFormulaToLogicAst validates node.kind
against known formula kinds (forall/exists/and/or/not/implies/predicate)
before passing through as FormulaNode. Previously any object with a
'kind' property was blindly cast.
predicates.ts / registry.ts: @deprecated tags on globalPredicateRegistry
and globalClauseRegistry. Both are still functional but consumers should
transition to explicit injection via LogicEngineOptions / EvaluationOptions.
454 solver+DSL tests pass, zero regressions.
- grammar.ts: add 'between' to isKeywordThatCanBePredicate() so the parser
recognizes it as a valid predicate keyword in forall/exists formula bodies
- compiler.ts: add special case in compileDenseFOLToFormula for
between(, min, max, dimension?) that extracts numeric args into
options ({min, max, dimension}) instead of dropping them in the generic
arg loop. The existing betweenPredicate evaluator already handles these.
- fol-dense-combinations.test.ts: replace GAP test with two verified-working
tests for between and between with dimension
- Add children branch in convertDomain() alongside descendants —
children() now correctly maps to parentVar: '$parent'
instead of falling through to the default branch which reversed
the parentVar/selector mapping.
- SelectorDomainResolver now handles parentVar domains with no
CSS selector filter (children domain collects all registered
subject IDs, then filters by parent via ancestor index).
- All 1072 tests pass across 14 packages.
- Add leftAlignedWith/rightAlignedWith/topAlignedWith/bottomAlignedWith
to lexer TokenKind union and keyword map
- Add to grammar.ts consumeRelation() recognized relation kinds
- Dense DSL users can now write: '.a' leftAlignedWith '.b', etc.
(previously returned parser error 'Expected relation')