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.
This commit is contained in:
John Dvorak
2026-05-22 11:55:58 -07:00
parent 0a73063c76
commit a424d29ccc
5 changed files with 36 additions and 31 deletions
+15 -18
View File
@@ -409,12 +409,11 @@ export async function extractWorldFastGeometry(
selectorToIds: Array<[string, number[]]>
}
const selectorPlans: SelectorPlan[] = await Promise.all(
selectors.map(async (key, i) => {
const queries = await materializeSemanticSelector(playwrightPage, key, i)
return { key, queries }
}),
)
const selectorPlans: SelectorPlan[] = []
for (let i = 0; i < selectors.length; i++) {
const queries = await materializeSemanticSelector(playwrightPage, selectors[i], i)
selectorPlans.push({ key: selectors[i], queries })
}
try {
const extracted = await playwrightPage.evaluate(({ plans, needs }: any) => {
@@ -800,12 +799,11 @@ export async function extractWorldCdp(
const errors: ImhotepDiagnostic[] = []
const selectorToNodeIds = new Map<string, number[]>()
const selectorPlans: SelectorPlan[] = await Promise.all(
selectors.map(async (key, i) => {
const queries = await materializeSemanticSelector(playwrightPage, key, i)
return { key, queries }
}),
)
const selectorPlans: SelectorPlan[] = []
for (let i = 0; i < selectors.length; i++) {
const queries = await materializeSemanticSelector(playwrightPage, selectors[i], i)
selectorPlans.push({ key: selectors[i], queries })
}
const sessionManager = createSessionManager(playwrightPage)
try {
@@ -993,12 +991,11 @@ export async function extractWorld(
if (requiredFacts?.styles) {
try {
const plans: SelectorPlan[] = await Promise.all(
filteredSelectors.map(async (key, i) => {
const queries = await materializeSemanticSelector(playwrightPage, key, i)
return { key, queries }
}),
)
const plans: SelectorPlan[] = []
for (let i = 0; i < filteredSelectors.length; i++) {
const queries = await materializeSemanticSelector(playwrightPage, filteredSelectors[i], i)
plans.push({ key: filteredSelectors[i], queries })
}
const chWidthsBySelector = await measureChWidthsByPlan(playwrightPage, plans)
attachMeasuredChWidths(result.world, result.selectorToIds, chWidthsBySelector)
} catch {