Commit Graph

20 Commits

Author SHA1 Message Date
John Dvorak 066ef9f677 refactor: remove global registry fallbacks — factory pattern for test isolation
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).
2026-05-22 15:44:44 -07:00
John Dvorak e78ffe3419 refactor: remove world smuggling — type styles, fragments, topologySubjectIds
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.
2026-05-22 14:38:37 -07:00
John Dvorak 45b5575e53 fix: prevent silent passing on missing topology data + visible cleanup failures
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.
2026-05-22 13:55:11 -07:00
John Dvorak 1a8f308f71 refactor: remove global mutable state — registry guard, compat warning, stats
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.
2026-05-22 13:52:09 -07:00
John Dvorak 9df295b915 refactor: eliminate remaining hardcoded predicate-name dispatch
Extraction.ts (3 fixes):
  - Replace 2 'inStackingContext' string checks with isVariableArityPredicate()
  - Replace 7-name diagnostic formatting if/else with spec-driven
    getPredicateSpec() checks (isDirectional → gap message,
    isSize → threshold hint, else generic)

Grammar.ts: Replace 8 hardcoded parser routing checks
  (atLeast/atMost/aspectRatio/between/clippedBy/attachedToScrollContainer/
  escapeClippingChainOf/inStackingContext) with SIZE_PREDICATE_NAMES and
  TOPOLOGY_PREDICATE_NAMES Sets derived from spec table.

Pipeline.ts: Replace 15-entry CODE_TO_CLAUSE_KIND map with runtime
  generation from PREDICATE_SPECS. Prefix derived from spec.isSize
  ('size.*') / validOptions.includes('axis') ('alignment.*') /
  else ('relation.*'). Manual override for aspectRatio code 15.

Proofs.ts: Replace 11-case switch(kind) with 5 spec-driven if/else
  branches categorized by validOptions presence (hasGap→directional,
  hasAxis→alignment) + 2 specific name checks (inside overflow,
  aspectRatio ratio). 11 predicate names → 0 hardcoded.

Lexer.ts: Export KEYWORDS map for conformance testing.

Conformance tests:
  - Solver: every BUILTIN_PREDICATES entry matches its PREDICATE_SPECS
    counterpart; every spec name (incl. aliases) has a registered
    evaluator with matching descriptor (2 tests)
  - DSL: every predicate name from collectAllPredicateNames() appears
    in the lexer KEYWORDS table (1 test)

598 SDK + 3 conformance + 57 E2E = 658 tests pass.
2026-05-22 13:15:35 -07:00
John Dvorak 9b691b2c7c fix: close out remaining audit findings — type safety, equivalence, deprecations
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.
2026-05-22 12:05:43 -07:00
John Dvorak e17e4d6c20 fix: remove silent error suppression — cache failures, box index mutation, cleanup catches
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.
2026-05-22 12:00:20 -07:00
John Dvorak a424d29ccc fix: remove design-debt shims — falsy ID bug, selector normalization, concurrency, exception swallowing
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.
2026-05-22 11:55:58 -07:00
John Dvorak 1bc92e1f7d fix: CDP extraction concurrency + topology ID remapping + predicate completeness
CDP protocol fixes:
- session.ts: getDocumentRootNodeId() now pushes {depth:-1} full DOM tree
  (was shallow default, causing querySelectorAll to find zero nodes)
- extraction.ts: serialize resolveSelector calls (was concurrent Promise.all,
  CDP DOM.querySelectorAll is not safe for concurrent calls on same session)

Topology ID mismatch fixes (critical — backendNodeId vs solver subject ID):
- extraction.ts: add remapTopologyIds() — builds Map<backendNodeId,solverId>
  from world.subjects.domNodeId/ids, remaps all 6 topology arrays +
  clipping.subjectId + scroll.containerId
- topology.ts: createsStackingContext() now checks contain:paint|layout
  (both create stacking contexts per CSS spec, was missing)
- topology.ts: evaluateInStackingContext() handles unary (no reference)

Predicate completeness:
- predicates.ts: 3 new evaluators — intersects, touches, hasGap (indices 31-33)
- predicates.ts: escapeClippingChainOf replaced indeterminate stub with
  deterministic 2-ary evaluator (overflow + clip chain + ancestry checks)
- predicates.ts: attachedToScrollContainer arity 1→2, checks reference match
- predicates.ts: 7 aliases + 4 alignment aliases promoted to BUILTIN_PREDICATES

DSL grammar fixes:
- grammar.ts: isKeywordThatCanBePredicate() extended with all missing keywords
- lower-to-canonical.ts: add TopologyAssertion lowering (was silently dropped)
- compiler.ts: add intersects/touches/hasGap relation codes
- validator.ts: add option sets for new predicates
- lexer.ts: add intersects/touches/hasGap token kinds

Safety + infrastructure:
- joins.ts: MAX_CARTESIAN_TUPLES=100,000 guard with descriptive error
- extraction.ts: computeRequiredFacts() registers defaults + broadens topology gate
- package.json: add missing imhotep-state dependency to imhotep-playwright

Tests:
- 9 new predicate tests (intersects, touches, hasGap, escapeClippingChainOf)
- Convert stale separatedFrom/contains GAP tests to working assertions
- Add computeRequiredFacts topology gating tests (public.test.ts)
2026-05-22 10:20:06 -07:00
John Dvorak 654becabc6 chore: remove dead makeNotImplementedPredicate helper
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.
2026-05-21 20:15:40 -07:00
John Dvorak 35c5dbd153 fix: add missing predicate evaluators and BUILTIN_PREDICATES entries
- 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.
2026-05-21 20:10:17 -07:00
John Dvorak 16dcf42762 fix: propagate operand metrics through boolean connectives
- evaluateAnd: carry left metrics on left-fail short-circuit,
  carry right metrics on right fail/indeterminate
- evaluateOr: carry left metrics on left-pass short-circuit,
  carry right metrics on right pass, merge both on double-fail
- evaluateNot: carry operand metrics when operand passes
  (so not fails with diagnostic context), carry on indeterminate
- evaluateImplies: carry consequent metrics on fail/indeterminate

This ensures compound formula failures preserve measured geometry
context (gap, dimensions, direction) rather than presenting empty
diagnostics to users investigating layout contract violations.
2026-05-21 17:14:00 -07:00
John Dvorak 5830d5861e fix: resolve inStackingContext arity/semantics mismatch
- 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
2026-05-21 17:10:38 -07:00
John Dvorak 19559b658b feat: implement variable-bound FOL domain resolution for descendants/children
- 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
2026-05-21 17:05:35 -07:00
John Dvorak de12e93cf8 chore: fill CSS contain test gaps + unskip clippedBy e2e test
- Add 4 unit tests in predicates.test.ts for new contain metrics:
  inside hasClippedOverflow (with/without clipping)
  clippedBy clipKind (contain:paint=1, overflow=2)
- Unskip and implement clippedBy e2e test with overflow:hidden container
  in e2e-edge.test.ts (was stale skipped with 'not yet implemented')
2026-05-21 14:44:26 -07:00
John Dvorak 96bcce1ddb feat: implement directional alignment predicate evaluators (no more NYI)
- 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
2026-05-21 14:36:02 -07:00
John Dvorak 8dae898304 feat: annotate inside/contains overflow metrics with clipping awareness
- 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)
2026-05-21 14:11:47 -07:00
John Dvorak c4a3d304ef feat: solver consumes CSS contain:paint for clippedBy diagnostics
- Add Contain=5 to geometry world ClipKind enum (avoids collision with
  topology engine's CONTAIN=3 vs Mask=3)
- Fix CDP isClippingElement() to detect contain:paint (style.contain)
- CDP topology builder sets clipKind=5 for contain:paint, 1 for overflow
- Enrich clippedByPredicate with clipKind metric:
  1=contain:paint, 2=overflow (hidden/scroll/auto)
- Reads world.clipping.clipKind of the reference's clip node
2026-05-21 13:52:28 -07:00
John Dvorak dd64e1e34a v1.1.0: repo polish, CI fixes, version alignment, dead artifact cleanup
Root package: renamed to imhotep-monorepo, fixed broken scripts (test:unit/integration/e2e),
removed inappropriate root deps, fixed build order, updated clean script

CI: branch trigger main->master, npm ci->npm install, GitHub cache URL->Gitea

Docs: replaced scaffolded root README with real project README, added package READMEs
for imhotep/imhotep-playwright/imhotep-dsl/imhotep-core, added RELEASE.md checklist

Version: all 14 packages and root aligned to 1.1.0, CHANGELOG test count fixed (1125)

Metadata: 14 repository URLs github->gitea, 13 package descriptions added,
imhotep-cli exports field added, SECURITY.md updated for Gitea+disclosure email

Quality: noEmitOnError:true in 13 tsconfigs, collapsed duplicate interfaces in public.ts,
clippedBy test->test.skip, fixed broken dynamic import in imhotep index.test.ts,
694 generated src artifacts cleaned, V8 logs removed, .gitignore updated
2026-05-21 10:10:11 -07:00
John Dvorak 92deb689cd v1.1.0: pooled runtime, 959 tests, production hardening (0 squash) 2025-08-15 10:00:00 -07:00