diff --git a/packages/imhotep-playwright/src/extraction.ts b/packages/imhotep-playwright/src/extraction.ts index 86df4df..3e25a64 100644 --- a/packages/imhotep-playwright/src/extraction.ts +++ b/packages/imhotep-playwright/src/extraction.ts @@ -83,6 +83,12 @@ const extractionPathStats = { cdpFallbacks: 0, } +/** Reset extraction path counters (for test isolation). */ +export function resetExtractionPathStats(): void { + extractionPathStats.fastPathHits = 0 + extractionPathStats.cdpFallbacks = 0 +} + const pageCacheNamespace = new WeakMap() let pageCacheNamespaceCounter = 0 @@ -95,7 +101,7 @@ function getPageCacheNamespace(page: Page): string { } let extractionStatsHookInstalled = false -let compatibilityWarningEmitted = false +const warnedUis = new WeakSet() function maybeInstallExtractionStatsHook(): void { if (extractionStatsHookInstalled) return @@ -1980,10 +1986,10 @@ export function buildCompatibilityReport(ui: ImhotepUi): CompatibilityReport { } export function maybeEmitCompatibilityWarning(ui: ImhotepUi): void { - if (compatibilityWarningEmitted) return + if (warnedUis.has(ui)) return const report = buildCompatibilityReport(ui) if (report.ok) return - compatibilityWarningEmitted = true + warnedUis.add(ui) const failed = report.checks.filter((c) => !c.ok) const detail = failed.map((c) => `${c.id}: ${c.details}`).join(' | ') const fingerprints = report.packageFingerprints diff --git a/packages/imhotep-solver/src/predicates.ts b/packages/imhotep-solver/src/predicates.ts index 6a8e306..df79a44 100644 --- a/packages/imhotep-solver/src/predicates.ts +++ b/packages/imhotep-solver/src/predicates.ts @@ -151,7 +151,6 @@ export function listRegisteredPredicates(): string[] { export function clearPredicateRegistry(): void { globalPredicateRegistry.clear(); - defaultPredicatesRegistered = false; } export function getPredicateDescriptor(name: string): PredicateDescriptor | undefined { @@ -1196,11 +1195,16 @@ export const hasGapPredicate: PredicateEvaluator = { // Register Defaults // --------------------------------------------------------------------------- -let defaultPredicatesRegistered = false; +/** Sentinel registered to detect if defaults were already installed. */ +const DEFAULT_SENTINEL = '__imhotep_defaults_registered__' export function registerDefaultPredicates(): void { - if (defaultPredicatesRegistered) return; - defaultPredicatesRegistered = true; + if (globalPredicateRegistry.get(DEFAULT_SENTINEL)) return + // Register sentinel first so partial failures don't cause infinite loops. + globalPredicateRegistry.register({ + descriptor: { name: DEFAULT_SENTINEL, arity: 0, domains: [], requiredFacts: [] }, + evaluateTuple: () => ({ truth: 'indeterminate' }), + }) registerPredicate(widthPredicate); registerPredicate(heightPredicate); registerPredicate(abovePredicate);