predicates.ts: Add populateDefaultPredicates(registry) accepting any
PredicateRegistry. Add createDefaultPredicateRegistry() factory.
registerDefaultPredicates() now delegates to populateDefaultPredicates
on the global (backward compatible).
logic-engine.ts: Replace globalPredicateRegistry fallback with
createDefaultPredicateRegistry() factory. Each evaluateLogic() call
creates a fresh self-populated registry unless one is explicitly
injected. No shared mutable state between evaluations.
engine.ts: Same pattern for clauses — add populateDefaultClauses(registry),
createDefaultClauseRegistry() factory. registerDefaultClauses() now
delegates to populateDefaultClauses on the global. evaluate() replaces
globalClauseRegistry fallback with createDefaultClauseRegistry().
registry.ts: @deprecated tag on registerClause with migration note.
Both global registries remain for backward compatibility via the
deprecated registerDefault*() functions, but the evaluation engines
no longer depend on them. Every evaluation gets its own registry by
default, so custom predicates registered by one test cannot leak
into another. Tests using explicit registry injection are unaffected.
662 tests pass (315 DSL + 141 core + 149 solver + 57 E2E).
registry.ts: Add optional styles and fragments table types to the
solver's GeometryWorld. Previously these were attached via
(world as any).styles / .fragments, bypassing the type system.
extraction.ts:
- remapTopologyIds: accept topologySubjectIds as explicit parameter
instead of reading (world as any)._topologySubjectIds and deleting
it post-remap. Eliminates 3 smuggling accesses (read, set, delete)
- All worldAny.styles / worldAny.fragments / worldAny.strings /
worldAny.transforms / worldAny.matrices assignments now use
typed world.xxx access. worldAny variable removed entirely.
- attachMeasuredChWidths: use world.styles directly.
- (result.world as any).env mutation replaced with typed
result.world.env assignment.
- (sizeWorld as any).styles replaced with typed access.
predicates.ts: Replace all 3 (world as any).styles inline type-assert
reads (getSubjectFontSizePx, getRootFontSizePx, getSubjectChWidthPx)
with direct world.styles?.xxx access. No runtime behavior change.
Zero (world as any) casts remain in extraction.ts or predicates.ts.
658 tests pass.
predicates.ts (missing-fact discipline):
- getTopologyValueBySubject: return -1 sentinel instead of ?? 0
when topology data is missing/unset. NaN and negative values
also treated as missing. Previously returned 0 which was
indistinguishable from root subject ID.
- clippedByPredicate: return 'indeterminate' when clippingRoot < 0
- inStackingContextPredicate: return 'indeterminate' when sc < 0
or refSC < 0 (both subject and reference). Previously treated
missing data as false — a silent wrong answer.
- attachedToScrollContainerPredicate: return 'indeterminate' when
scrollContainer < 0
- escapeClippingChainOfPredicate: return 'indeterminate' when
clippingRoot < 0
extraction.ts (cleanup visibility):
- Promote fast-geometry and CDP cleanup failures from console.debug
(invisible during test execution) to console.warn. Contaminated
pages are now diagnosable without debug-log inspection.
658 tests pass.
predicates.ts: Replace defaultPredicatesRegistered boolean guard with
sentinel predicate check inside the registry. registerDefaultPredicates()
is now always safe to call — no module-scope flag that can drift out
of sync with the actual registry state. clearPredicateRegistry() no
longer needs to manually reset a flag.
extraction.ts:
- Replace compatibilityWarningEmitted process-singleton boolean with
WeakSet<ImhotepUi> (warnedUis). Each ImhotepUi instance now gets its
own compatibility warning, fixing the bug where two pages would share
a single warning gate.
- Export resetExtractionPathStats() for test isolation of fast-path
and CDP fallback counters.
598 SDK + 3 conformance + 57 E2E = 658 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.
geometry-cache.ts: replace 5 empty catch blocks with console.warn
- statSync failure, rmSync failure (x2), readCachedWorld failure,
readCachedExtractionResult failure were all silently swallowed.
Now emit context-bearing warnings so stale/corrupt caches are visible.
predicates.ts: replace __boxIndex as any mutation with WeakMap
- getBorderRect used (world as any).__boxIndex to cache a subject-to-
box-index map on the world object. Replaced with module-level WeakMap
that auto-collects when the world is GC'd. Eliminates 2 as any casts.
extraction.ts: serialize materializeSemanticSelector + debug cleanup
- 3 Promise.all sites over page.evaluate changed to sequential for..of
to eliminate DOM modification race conditions.
- 2 .catch(()=>{}) cleanup blocks now use console.debug so failed
cleanup is traceable when debugging.
- resolveViewport catch now emits console.warn on zero-viewport fallback.
648 SDK + 57 E2E tests pass.
All 27 BUILTIN_PREDICATES now have real evaluators. The
makeNotImplementedPredicate factory and its IMH_FEATURE_NOT_YET_IMPLEMENTED
path were the last remaining NYI scaffolding — no caller existed for it.
IMH_FEATURE_NOT_YET_IMPLEMENTED diagnostic code kept in the taxonomy
as a future fallback, but no evaluator produces it.
- CRITICAL: escapeClippingChainOf now has an evaluator (returns indeterminate
with clear diagnostic — full implementation requires fragment-level bounds
analysis). Previously parsed but silently produced IMH_EVALUATOR_MISSING.
- attachedToScrollContainer added to BUILTIN_PREDICATES with evaluator
(checks world.topology.scrollContainerOf). Was only in legacy engine.
- aspectRatio added to BUILTIN_PREDICATES with evaluator (compares
width/height ratio against min/max bounds). Was only in legacy engine.
- Fix all BUILTIN_PREDICATES index references shifted by new entries
(beside 17→20, nextTo 18→21, adjacent 19→22, touching 20→23,
near 21→24, under 22→25, within 23→26, separatedFrom 16→18,
inStackingContext 15→17).
- Register attachedToScrollContainer, escapeClippingChainOf, aspectRatio
in registerDefaultPredicates.
- Zero NYI/not-implemented predicates remain in the registry.
- Change BUILTIN_PREDICATES[15] arity from 1 to 2 to match binary usage
in the FOL compiler and topology engine
- Update evaluator to handle both unary (does element have stacking context)
and binary (do both elements share the same stacking context) cases
- Binary comparison: both elements must have a valid stacking context
AND their stackingContextOf values must match
- Aligns predicate semantics with the legacy engine in solver/topology.ts
and the topology query layer's inStackingContext(graph, subj, ref?) API
- Replace makeNotImplementedPredicate stubs for leftAlignedWith,
rightAlignedWith, topAlignedWith, bottomAlignedWith with real
evaluators delegating to alignedWithPredicate with axis option
- Update logic-engine.test.ts to expect real evaluation results
instead of IMH_FEATURE_NOT_YET_IMPLEMENTED
- insidePredicate: add hasClippedOverflow=1 when reference element
clips its content (contain:paint or overflow:hidden) and the
subject overflows beyond the reference bounds
- containsPredicate: same, checking the subject (container) clips
- Reads world.clipping table to determine if the container clips
- Safe when clipping data is absent (unit test fixtures)