Commit Graph

19 Commits

Author SHA1 Message Date
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