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.
pipeline.ts: || undefined → ?? undefined (9 occurrences)
- || converts valid subject ID 0 to undefined because 0 is falsy in JS.
This broke clause witnesses and topology references for the first subject.
domain-index.ts: remove .toLowerCase() on CSS selectors
- CSS selectors are case-sensitive (IDs, class names, attribute values).
Lowercasing on lookup but not on storage (selectorIndex) meant case-
sensitive selectors never matched — returning empty arrays silently.
canonical.ts: add warning when visualBoxes falls back to layout boxes
- visualBoxes ?? boxes silently substituted layout coordinates for visual
space, producing incorrect results for transform-dependent assertions.
Now emits console.warn so silent data corruption is visible.
extraction.ts: serialize materializeSemanticSelector calls (3 sites)
- Changed Promise.all over page.evaluate() to sequential for..of. While
Playwright serializes CDP calls internally, concurrent DOM-modifying
evaluate() calls create undefined execution order. Sequential resolution
eliminates theoretical race conditions for semantic selector injection.
engine.ts: include stack trace in evaluator exception diagnostics
- Catch-all converted ALL exceptions (including TypeError from programming
bugs) to IMH_EVALUATOR_EXCEPTION with just err.message. Now includes
stack trace and logs to console.warn for visibility. Distinguishes
TypeError (programming bug) from other evaluation errors.
648 SDK tests + 57 E2E hard tests pass, zero regressions.
Topology array order mismatch (critical bug):
- CDP browser script iterates elements in document order (querySelectorAll),
but solver accesses topology arrays by selector-resolution order.
- Fix: add subjectIds array to TopologyRecord tracking the backendNodeId
at each document-order position; remapTopologyIds now reorders all 6
topology arrays before remapping backendNodeIds to solver IDs.
- Fallback: when subjectIds is missing (cached pre-fix data), falls back
to simple remap without reordering.
Unary inStackingContext compilation:
- compileCanonicalClauseToFormula treated inStackingContext as always binary,
creating referenceBinding with undefined selector for unary assertions.
- Fix: add (inStackingContext && !clause.reference) to unaryPredicate/isUnary.
Hard test fixture (fixtures package):
- 29-element geometric fixture with 10 scenarios
- 57 assertions (54 spatial + 3 topology) all pass end-to-end
- 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.
- computeRequiredFacts now returns domAncestry flag by scanning formula
bindings for parentVar references (descendants/children domains)
- extractWorld fast-path gate now requires domAncestry === false — formulas
with parentVar domains will always use CDP extraction, which provides
the DOM parentNodeId data needed for ancestor index construction
- Prevents silent indeterminate results when descendants(, sel) is
used on the fast path (which lacks DOM ancestry data)
- Cache key updated to include domAncestry flag ('a') so cached fast vs
CDP results for the same selectors don't collide
- mapFolDiagnostic now accepts optional metrics parameter
- adaptFOLResultToImhotepResult matches solver diagnostics to
formula results by clauseId/formulaId and passes formula-level
metrics (gap, dimensions, stacking context, overflow, etc.)
into the diagnostics output
- Previously mapFolDiagnostic always returned metrics: {} and
sourceRef: {}, making compound/quantified failures undiagnosable
for users investigating contracts
- Extend DomainResolver.resolve() signature to accept optional BindingEnv
so that parentVar domains can be resolved with runtime variable bindings
- Pass BindingEnv through evaluateForAll/evaluateExists to resolver calls
- Add buildAncestorIndex() to precompute DOM ancestor sets from CDP data
- SelectorDomainResolver now filters descendant domains by the bound parent
when domain.parentVar is present and ancestor index is available
- Return undefined for parentVar domains when no ancestor index or env
(prevents silent fallback to global domain resolution)
- Update all test DomainResolver mocks for new resolve interface
- Add 10 unit tests covering ancestor index construction, backward compat,
descendant filtering, exclusion of non-descendants, empty descendants,
missing parentVar/env, and no-ancestor-index safety
Moved the extraction pipeline, formula analysis, selector resolution glue,
CDP extraction, canonical compilation, contract building, cardinality
evaluation, FOL diagnostic mapping, compatibility reporting, and all
module-level extraction state into a dedicated extraction.ts module.
public.ts reduced from 3568 to 1533 lines (-57%). The remaining file
contains only the imhotep() entry point, property-run internals, and
component/story/fixture entry points, plus re-exports for backward
compatibility.