@detiq/crawler 1.1.23 → 1.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/crawler.js +173 -28
  2. package/dist/crawler.js.map +2 -2
  3. package/dist/extractors/branding-extractor.js +104 -0
  4. package/dist/extractors/branding-extractor.js.map +7 -0
  5. package/dist/extractors/summary-extractor.js +36 -0
  6. package/dist/extractors/summary-extractor.js.map +7 -0
  7. package/dist/frontier.js +20 -2
  8. package/dist/frontier.js.map +2 -2
  9. package/dist/incremental-diff.js +76 -0
  10. package/dist/incremental-diff.js.map +2 -2
  11. package/dist/index.js +3 -1
  12. package/dist/index.js.map +2 -2
  13. package/dist/interactive-probe.js +108 -17
  14. package/dist/interactive-probe.js.map +2 -2
  15. package/dist/job-queue.js +408 -61
  16. package/dist/job-queue.js.map +2 -2
  17. package/dist/sqs-queue.js +78 -10
  18. package/dist/sqs-queue.js.map +2 -2
  19. package/dist/worker.js +7 -7
  20. package/dist/worker.js.map +2 -2
  21. package/package.json +1 -1
  22. package/src/crawler.ts +208 -30
  23. package/src/extractors/branding-extractor.ts +159 -0
  24. package/src/extractors/summary-extractor.ts +49 -0
  25. package/src/frontier.ts +31 -1
  26. package/src/incremental-diff.ts +138 -0
  27. package/src/index.ts +1 -1
  28. package/src/interactive-probe.ts +129 -20
  29. package/src/job-queue.ts +489 -63
  30. package/src/sqs-queue.ts +92 -12
  31. package/src/worker.ts +13 -11
  32. package/types/crawler.d.ts +20 -0
  33. package/types/extractors/branding-extractor.d.ts +46 -0
  34. package/types/extractors/summary-extractor.d.ts +10 -0
  35. package/types/frontier.d.ts +9 -1
  36. package/types/incremental-diff.d.ts +57 -0
  37. package/types/index.d.ts +1 -1
  38. package/types/interactive-probe.d.ts +5 -1
  39. package/types/job-queue.d.ts +53 -0
  40. package/types/sqs-queue.d.ts +12 -1
@@ -95,21 +95,43 @@ async function findModalTriggers(page) {
95
95
  return page.evaluate(() => {
96
96
  const SKIP = /submit|send|save|create|log.?in|sign.?in|register|continue|checkout|buy|order|search|close|cancel|dismiss/i;
97
97
  const NAV_PARENTS = /* @__PURE__ */ new Set(["NAV", "HEADER", "FOOTER"]);
98
- return Array.from(document.querySelectorAll('button, [role="button"]')).filter((el) => {
99
- const style = window.getComputedStyle(el);
100
- if (style.display === "none" || style.visibility === "hidden") return false;
101
- const label = el.innerText?.trim() || el.getAttribute("aria-label") || "";
102
- if (!label || SKIP.test(label)) return false;
98
+ function isVisible(el) {
99
+ const s = window.getComputedStyle(el);
100
+ return s.display !== "none" && s.visibility !== "hidden" && s.opacity !== "0";
101
+ }
102
+ const seen = /* @__PURE__ */ new Set();
103
+ const results = [];
104
+ for (const el of Array.from(document.querySelectorAll('button, [role="button"]'))) {
105
+ if (!isVisible(el)) continue;
106
+ const label = (el.innerText?.trim() || el.getAttribute("aria-label") || "").slice(0, 60);
107
+ if (!label || SKIP.test(label) || seen.has(label)) continue;
103
108
  let p = el.parentElement;
109
+ let inNav = false;
104
110
  while (p) {
105
- if (NAV_PARENTS.has(p.tagName)) return false;
111
+ if (NAV_PARENTS.has(p.tagName)) {
112
+ inNav = true;
113
+ break;
114
+ }
106
115
  p = p.parentElement;
107
116
  }
108
- return true;
109
- }).slice(0, 20).map((el) => {
110
- const label = el.innerText?.trim() || el.getAttribute("aria-label") || "button";
111
- return { selector: `[aria-label="${label}"]`, label: label.slice(0, 60) };
112
- });
117
+ if (inNav) continue;
118
+ seen.add(label);
119
+ results.push({ selector: `button:has-text("${label.slice(0, 40)}")`, label });
120
+ if (results.length >= 15) break;
121
+ }
122
+ const rowCandidates = Array.from(document.querySelectorAll(
123
+ '[role="row"][tabindex="0"], tr[tabindex="0"], [role="listitem"][tabindex="0"], [class*="list-item"][tabindex="0"], [class*="table-row"][tabindex="0"]'
124
+ )).filter(isVisible).slice(0, 3);
125
+ for (const row of rowCandidates) {
126
+ const ariaLabel = row.getAttribute("aria-label");
127
+ const textLabel = row.innerText?.trim().split("\n")[0].trim().slice(0, 60);
128
+ const label = (ariaLabel || textLabel || "").slice(0, 60);
129
+ if (!label || seen.has(label)) continue;
130
+ seen.add(label);
131
+ const selector = ariaLabel ? `[aria-label="${ariaLabel}"]` : `[role="row"]:has-text("${label.slice(0, 40)}")`;
132
+ results.push({ selector, label });
133
+ }
134
+ return results.slice(0, 20);
113
135
  });
114
136
  }
115
137
  async function probeConditionals(page) {
@@ -130,7 +152,17 @@ async function probeConditionals(page) {
130
152
  return Array.from(document.querySelectorAll('[data-conditional],[data-show],[class*="conditional"]')).filter((el) => window.getComputedStyle(el).display !== "none").map((el) => el.innerText?.trim()).filter((t) => t && t.length > 10).slice(0, 3).join(" | ");
131
153
  });
132
154
  if (revealed) {
133
- results.push({ triggerElement: label, triggerValue: opt.text || opt.value, revealedText: revealed.slice(0, 200) });
155
+ const domSnapshot = await page.content();
156
+ const stateId = await page.evaluate(() => {
157
+ let hash = 0;
158
+ const str = document.body.innerText;
159
+ for (let i = 0; i < str.length; i++) {
160
+ hash = (hash << 5) - hash + str.charCodeAt(i);
161
+ hash = hash & hash;
162
+ }
163
+ return Math.abs(hash).toString(16);
164
+ });
165
+ results.push({ triggerElement: label, triggerValue: opt.text || opt.value, revealedText: revealed.slice(0, 200), stateId, domSnapshot });
134
166
  }
135
167
  }
136
168
  }
@@ -232,10 +264,17 @@ async function probeScreen(page, screenId, url, elements) {
232
264
  const triggers = await findModalTriggers(page);
233
265
  for (const trigger of triggers) {
234
266
  try {
235
- const btn = page.locator(`button:has-text("${trigger.label}")`).first();
267
+ const btn = page.locator(trigger.selector).first();
236
268
  const domBefore = await page.evaluate(() => document.body.innerText.length);
237
269
  await btn.click({ timeout: 1500 });
238
- await page.waitForTimeout(1e3);
270
+ try {
271
+ await page.waitForSelector(
272
+ '[role="dialog"],[role="alertdialog"],[aria-modal="true"],[data-state="open"],[class*="modal" i]:not([class*="backdrop" i]),[class*="Dialog"]:not([class*="Overlay"])',
273
+ { timeout: 8e3, state: "visible" }
274
+ );
275
+ } catch {
276
+ await page.waitForTimeout(300);
277
+ }
239
278
  const dialogText = await page.evaluate(() => {
240
279
  const DIALOG_SEL = '[role="dialog"], [role="alertdialog"], .modal, .Modal, [class*="modal" i], [class*="dialog" i], [class*="Dialog"]';
241
280
  const dialogs = Array.from(document.querySelectorAll(DIALOG_SEL)).filter((el) => window.getComputedStyle(el).display !== "none");
@@ -246,12 +285,24 @@ async function probeScreen(page, screenId, url, elements) {
246
285
  const fields = Array.from(d.querySelectorAll("input, select, textarea, button")).map((f) => f.getAttribute("aria-label") || f.getAttribute("placeholder") || f.value || "").filter((t) => t.trim().length > 0);
247
286
  return { title, content, fields };
248
287
  });
288
+ const domSnapshot = await page.content();
289
+ const stateId = await page.evaluate(() => {
290
+ let hash = 0;
291
+ const str = document.body.innerText;
292
+ for (let i = 0; i < str.length; i++) {
293
+ hash = (hash << 5) - hash + str.charCodeAt(i);
294
+ hash = hash & hash;
295
+ }
296
+ return Math.abs(hash).toString(16);
297
+ });
249
298
  if (dialogText && dialogText.content.length > 20) {
250
299
  result.modals.push({
251
300
  triggerElement: trigger.label,
252
301
  title: dialogText.title,
253
302
  content: dialogText.content,
254
- fields: dialogText.fields.slice(0, 10)
303
+ fields: dialogText.fields.slice(0, 10),
304
+ stateId,
305
+ domSnapshot
255
306
  });
256
307
  } else {
257
308
  const accordionText = await page.evaluate(() => {
@@ -272,7 +323,9 @@ async function probeScreen(page, screenId, url, elements) {
272
323
  triggerElement: trigger.label,
273
324
  title: accordionText.heading || `${trigger.label} (revealed)`,
274
325
  content: accordionText.content,
275
- fields: []
326
+ fields: [],
327
+ stateId,
328
+ domSnapshot
276
329
  });
277
330
  }
278
331
  }
@@ -446,7 +499,7 @@ ${m.content}`).join("\n") : ""
446
499
  interactionPatternsChunk: interactionSection
447
500
  };
448
501
  }
449
- async function probeAllScreens(page, tenantId, projectId, discovered, onProgress) {
502
+ async function probeAllScreens(page, tenantId, projectId, discovered, onProgress, jobId) {
450
503
  const probeable = discovered.filter((d) => d.screenId && d.url && !d.url.includes("#"));
451
504
  if (probeable.length === 0) return;
452
505
  const errorCatalogueChunks = [];
@@ -472,6 +525,44 @@ async function probeAllScreens(page, tenantId, projectId, discovered, onProgress
472
525
  if (probeMarkdown.length > 50) {
473
526
  await AppBrain.saveKnowledgeChunk(tenantId, projectId, "probe", screen.screenId, probeMarkdown, `Probe \u2014 ${screen.url}`).catch(console.warn);
474
527
  }
528
+ const virtualScreensToSave = [];
529
+ for (const m of result.modals) {
530
+ if (m.stateId) {
531
+ virtualScreensToSave.push({
532
+ stateId: m.stateId,
533
+ triggerAction: `click ${m.triggerElement}`,
534
+ overlayType: "modal"
535
+ });
536
+ const modalUrl = `${screen.url}#probe:${m.stateId}`;
537
+ const modalTitle = m.title || `${m.triggerElement} Modal`;
538
+ AppBrain.saveScreen(tenantId, projectId, modalTitle, modalUrl, jobId, "INTERACTIVE_PROBE").then((modalScreen) => {
539
+ if (modalScreen?.id && screen.screenId) {
540
+ AppBrain.saveDiscoveredLinks(tenantId, projectId, screen.screenId, [{
541
+ href: modalUrl,
542
+ text: m.triggerElement,
543
+ linkType: "BUTTON_LINK",
544
+ source: "INTERACTIVE",
545
+ toScreenId: modalScreen.id,
546
+ jobId
547
+ }]).catch(() => {
548
+ });
549
+ }
550
+ }).catch(() => {
551
+ });
552
+ }
553
+ }
554
+ for (const c of result.conditionals) {
555
+ if (c.stateId) {
556
+ virtualScreensToSave.push({
557
+ stateId: c.stateId,
558
+ triggerAction: `change ${c.triggerElement} to ${c.triggerValue}`,
559
+ overlayType: "panel"
560
+ });
561
+ }
562
+ }
563
+ if (virtualScreensToSave.length > 0) {
564
+ await AppBrain.saveVirtualScreens(tenantId, projectId, screen.screenId, virtualScreensToSave).catch(console.warn);
565
+ }
475
566
  if (result.validationErrors.length > 0) {
476
567
  console.log(`[probe] ${screen.url}: ${result.validationErrors.length} validation errors, ${result.modals.length} modals`);
477
568
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/interactive-probe.ts"],
4
- "sourcesContent": ["/**\n * Interactive Probe \u2014 Phase 2 Dynamic Observation\n *\n * The static crawl captures DOM structure at rest. This module runs AFTER the static crawl\n * to observe dynamic behaviour by actually interacting with the UI:\n *\n * 1. Empty submit \u2192 captures required-field validation error messages\n * 2. Invalid format \u2192 captures format validation errors (bad email, short password)\n * 3. Button clicks \u2192 captures modal/dialog contents\n * 4. Select changes \u2192 captures conditional reveals (show/hide logic)\n * 5. Success paths \u2192 captures toast/banner messages (where input is safe)\n *\n * Results are stored as KnowledgeChunks (sourceType='probe') and update the\n * ERROR_CATALOGUE + INTERACTION_PATTERNS skill files with real observed data\n * instead of LLM inference.\n *\n * Call signature: probeScreen(page, screenId, elements, opts)\n * Called from crawlProject() after static element extraction when opts.interactive=true.\n */\n\nimport type { Page } from 'playwright';\nimport { AppBrain } from '@detiq/app-brain';\n\nexport interface ProbeElementFact {\n meaning: string;\n role: string;\n expectedData?: string;\n}\n\nexport interface ValidationError {\n field: string;\n trigger: 'empty_submit' | 'invalid_format' | 'boundary';\n errorText: string;\n}\n\nexport interface SuccessMessage {\n type: 'toast' | 'inline_success' | 'redirect';\n content: string;\n}\n\nexport interface ModalObservation {\n triggerElement: string;\n title: string;\n content: string;\n fields: string[];\n}\n\nexport interface ConditionalReveal {\n triggerElement: string;\n triggerValue: string;\n revealedText: string;\n}\n\nexport interface TooltipCapture {\n triggerLabel: string;\n tooltipText: string;\n}\n\nexport interface TabPanelCapture {\n tabLabel: string;\n panelContent: string;\n panelHeading?: string;\n}\n\nexport interface CarouselSlide {\n slideIndex: number;\n heading?: string;\n content: string;\n}\n\nexport interface WizardStep {\n stepIndex: number;\n stepLabel?: string;\n content: string;\n fields: string[];\n}\n\nexport interface FocusTrapResult {\n modalTrigger: string;\n focusTrapped: boolean;\n sampledFocused: string[];\n}\n\nexport interface ContextMenuCapture {\n trigger: string;\n items: string[];\n}\n\nexport interface ProbeResult {\n screenId: string;\n url: string;\n validationErrors: ValidationError[];\n successMessages: SuccessMessage[];\n modals: ModalObservation[];\n conditionals: ConditionalReveal[];\n loadingPatterns: string[];\n tooltips?: TooltipCapture[];\n tabPanels?: TabPanelCapture[];\n carouselSlides?: CarouselSlide[];\n wizardSteps?: WizardStep[];\n focusTraps?: FocusTrapResult[];\n skipNavIssues?: string[];\n contextMenus?: ContextMenuCapture[];\n}\n\n// \u2500\u2500 CSS selectors that reliably find error messages across frameworks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst ERROR_SELECTORS = [\n '[role=\"alert\"]',\n '[aria-live=\"assertive\"]',\n '[aria-live=\"polite\"]',\n '.invalid-feedback',\n '.field-error',\n '.error-message',\n '.form-error',\n '.validation-error',\n '[class*=\"error\" i]',\n '[class*=\"invalid\" i]',\n '[class*=\"Error\"]',\n 'p.text-red-500', 'p.text-red-600', 'p.text-destructive', 'p.text-danger',\n '[data-error]', '[data-invalid]',\n '.ant-form-item-explain-error', // Ant Design\n '.el-form-item__error', // Element UI\n '.chakra-form__error-message', // Chakra UI\n '.mantine-InputWrapper-error', // Mantine\n].join(', ');\n\n// \u2500\u2500 Toast / notification selectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst TOAST_SELECTORS = [\n '[role=\"status\"]',\n '[data-sonner-toast]',\n '[data-toast]',\n '[data-hot-toast]',\n '.Toastify__toast',\n '.toast-message',\n '.notification-body',\n '.notistack-snackbar',\n '[class*=\"toast\" i]',\n '[class*=\"Toast\"]',\n '[class*=\"snackbar\" i]',\n '[class*=\"notification\" i]',\n].join(', ');\n\n/** Wait up to `maxMs` for any selector to appear, then return its text content. */\nasync function waitForAny(page: Page, selector: string, maxMs = 2500): Promise<string[]> {\n try {\n await page.waitForSelector(selector, { timeout: maxMs, state: 'visible' });\n } catch { return []; }\n return page.$$eval(selector, (els) =>\n els.map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim()).filter(Boolean)\n );\n}\n\n/** Collect all currently-visible error messages. */\nasync function collectErrors(page: Page): Promise<string[]> {\n try {\n return page.$$eval(ERROR_SELECTORS, (els) =>\n els\n .filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden' && (el as HTMLElement).innerText?.trim().length > 0;\n })\n .map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim())\n .filter(Boolean)\n );\n } catch { return []; }\n}\n\n/** Collect any visible toast / notification content. */\nasync function collectToasts(page: Page): Promise<string[]> {\n try {\n return page.$$eval(TOAST_SELECTORS, (els) =>\n els\n .filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && (el as HTMLElement).innerText?.trim().length > 0;\n })\n .map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim())\n .filter(Boolean)\n );\n } catch { return []; }\n}\n\n/** Find the primary submit button on the page. */\nasync function findSubmitButton(page: Page): Promise<string | null> {\n return page.evaluate(() => {\n const SUBMIT_KW = /submit|send|save|create|confirm|continue|proceed|register|sign.?in|log.?in|apply|verify|search|next|finish|complete|order|buy|checkout/i;\n const candidates = Array.from(document.querySelectorAll('button, [role=\"button\"], input[type=\"submit\"]')) as HTMLElement[];\n const primary = candidates.find((el) => {\n const style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') return false;\n const text = el.innerText || (el as HTMLInputElement).value || el.getAttribute('aria-label') || '';\n return SUBMIT_KW.test(text);\n });\n if (!primary) return null;\n return primary.getAttribute('data-testid') || primary.getAttribute('aria-label') || primary.innerText?.trim().slice(0, 60);\n });\n}\n\n/** Find all modal-trigger buttons (non-submit buttons). */\nasync function findModalTriggers(page: Page): Promise<Array<{ selector: string; label: string }>> {\n return page.evaluate(() => {\n const SKIP = /submit|send|save|create|log.?in|sign.?in|register|continue|checkout|buy|order|search|close|cancel|dismiss/i;\n const NAV_PARENTS = new Set(['NAV', 'HEADER', 'FOOTER']);\n return Array.from(document.querySelectorAll('button, [role=\"button\"]'))\n .filter((el) => {\n const style = window.getComputedStyle(el as HTMLElement);\n if (style.display === 'none' || style.visibility === 'hidden') return false;\n const label = (el as HTMLElement).innerText?.trim() || el.getAttribute('aria-label') || '';\n if (!label || SKIP.test(label)) return false;\n // Skip nav chrome\n let p = el.parentElement;\n while (p) { if (NAV_PARENTS.has(p.tagName)) return false; p = p.parentElement; }\n return true;\n })\n .slice(0, 20) // probe at most 20 buttons per screen\n .map((el) => {\n const label = (el as HTMLElement).innerText?.trim() || el.getAttribute('aria-label') || 'button';\n return { selector: `[aria-label=\"${label}\"]`, label: label.slice(0, 60) };\n });\n });\n}\n\n/** Try to interact with select elements and observe conditional reveals. */\nasync function probeConditionals(page: Page): Promise<ConditionalReveal[]> {\n const results: ConditionalReveal[] = [];\n try {\n const selects = await page.$$('select');\n for (const sel of selects.slice(0, 5)) {\n const label = await sel.getAttribute('aria-label') || await sel.getAttribute('name') || 'select';\n const options = await sel.$$eval('option', (opts) => opts.slice(1, 3).map((o) => ({ value: o.value, text: o.textContent?.trim() })));\n for (const opt of options) {\n if (!opt.value) continue;\n const domBefore = await page.evaluate(() => document.body.innerText.length);\n await sel.selectOption(opt.value);\n await page.waitForTimeout(500);\n const domAfter = await page.evaluate(() => document.body.innerText.length);\n if (Math.abs(domAfter - domBefore) > 50) {\n const revealed = await page.evaluate(() => {\n // Grab text from any newly-visible sections\n return Array.from(document.querySelectorAll('[data-conditional],[data-show],[class*=\"conditional\"]'))\n .filter((el) => window.getComputedStyle(el).display !== 'none')\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter((t) => t && t.length > 10)\n .slice(0, 3)\n .join(' | ');\n });\n if (revealed) {\n results.push({ triggerElement: label, triggerValue: opt.text || opt.value, revealedText: revealed.slice(0, 200) });\n }\n }\n }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/**\n * Run interactive probing on a single screen page.\n * The page must already be navigated to the screen URL.\n * `elements` is the output of the static extractElements pass.\n */\nexport async function probeScreen(\n page: Page,\n screenId: string,\n url: string,\n elements: ProbeElementFact[],\n): Promise<ProbeResult> {\n const result: ProbeResult = {\n screenId, url,\n validationErrors: [], successMessages: [], modals: [], conditionals: [], loadingPatterns: [],\n };\n\n const formInputs = elements.filter((e) =>\n /input|text|email|password|tel|search|textarea|select/i.test(e.role)\n );\n const hasForm = formInputs.length > 0;\n\n // \u2500\u2500 Probe 1: Empty submit \u2192 capture required-field errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (hasForm) {\n try {\n const submitLabel = await findSubmitButton(page);\n if (submitLabel) {\n // Click submit with no data filled in\n const submitBtn = page.locator(`button:has-text(\"${submitLabel}\")`).first();\n await submitBtn.click({ timeout: 2000 });\n await page.waitForTimeout(1500);\n\n const errors = await collectErrors(page);\n // Prefer DOM-proximity mapping via aria-invalid + aria-describedby\n const ariaFieldErrors = await page.evaluate(() =>\n Array.from(document.querySelectorAll('[aria-invalid=\"true\"]')).map((el) => {\n const describedBy = el.getAttribute('aria-describedby');\n const errorEl = describedBy ? document.getElementById(describedBy) : null;\n const label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || (el as HTMLInputElement).name || '';\n return { label, errorText: errorEl?.textContent?.trim() || '' };\n }).filter((f) => f.errorText)\n );\n const ariaMatched = new Set<string>();\n for (const af of ariaFieldErrors) {\n ariaMatched.add(af.errorText);\n result.validationErrors.push({ field: af.label || 'unknown', trigger: 'empty_submit', errorText: af.errorText.slice(0, 150) });\n }\n // Fallback: text-proximity match for errors not covered by aria-invalid\n for (const errorText of errors.filter((e) => !ariaMatched.has(e))) {\n const matchedField = formInputs.find((el) =>\n errorText.toLowerCase().includes(el.meaning.toLowerCase().split(' ')[0]) ||\n el.meaning.toLowerCase().includes(errorText.toLowerCase().split(' ')[0])\n );\n result.validationErrors.push({\n field: matchedField?.meaning ?? 'unknown',\n trigger: 'empty_submit',\n errorText: errorText.slice(0, 150),\n });\n }\n\n // Check for loading state text during submit\n const loadingText = await page.evaluate(() => {\n const loading = Array.from(document.querySelectorAll('button, [aria-busy=\"true\"], [class*=\"loading\" i], [class*=\"spinner\" i]'))\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter((t) => t && /loading|saving|submitting|sending|processing/i.test(t));\n return loading.slice(0, 3);\n });\n result.loadingPatterns.push(...loadingText);\n }\n } catch { /* non-fatal \u2014 form probe failed, continue */ }\n }\n\n // \u2500\u2500 Probe 2: Invalid format inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (hasForm) {\n try {\n const emailField = formInputs.find((e) => e.expectedData === 'valid_email' || /email/i.test(e.meaning));\n const passwordField = formInputs.find((e) => e.expectedData === 'password' || /password/i.test(e.meaning));\n\n if (emailField) {\n const emailLocator = page.locator('input[type=\"email\"], input[name*=\"email\"], input[placeholder*=\"email\" i]').first();\n try {\n await emailLocator.fill('notanemail', { timeout: 1000 });\n await emailLocator.blur();\n await page.waitForTimeout(600);\n const errors = await collectErrors(page);\n for (const errorText of errors) {\n result.validationErrors.push({ field: emailField.meaning, trigger: 'invalid_format', errorText: errorText.slice(0, 150) });\n }\n } catch { /* locator not found */ }\n }\n\n if (passwordField) {\n const pwLocator = page.locator('input[type=\"password\"]').first();\n try {\n await pwLocator.fill('123', { timeout: 1000 });\n await pwLocator.blur();\n await page.waitForTimeout(600);\n const errors = await collectErrors(page);\n for (const errorText of errors) {\n if (!result.validationErrors.some((e) => e.errorText === errorText)) {\n result.validationErrors.push({ field: passwordField.meaning, trigger: 'invalid_format', errorText: errorText.slice(0, 150) });\n }\n }\n } catch { /* locator not found */ }\n }\n } catch { /* non-fatal */ }\n }\n\n // \u2500\u2500 Probe 3: Button clicks \u2192 discover modal/dialog contents \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n const triggers = await findModalTriggers(page);\n for (const trigger of triggers) {\n try {\n const btn = page.locator(`button:has-text(\"${trigger.label}\")`).first();\n const domBefore = await page.evaluate(() => document.body.innerText.length);\n\n await btn.click({ timeout: 1500 });\n await page.waitForTimeout(1000);\n\n // Check if a dialog/modal appeared\n const dialogText = await page.evaluate(() => {\n const DIALOG_SEL = '[role=\"dialog\"], [role=\"alertdialog\"], .modal, .Modal, [class*=\"modal\" i], [class*=\"dialog\" i], [class*=\"Dialog\"]';\n const dialogs = Array.from(document.querySelectorAll(DIALOG_SEL))\n .filter((el) => window.getComputedStyle(el).display !== 'none');\n if (dialogs.length === 0) return null;\n const d = dialogs[0] as HTMLElement;\n const title = d.querySelector('[role=\"heading\"], h1, h2, h3')?.textContent?.trim() || '';\n const content = d.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400);\n const fields = Array.from(d.querySelectorAll('input, select, textarea, button'))\n .map((f) => (f as HTMLElement).getAttribute('aria-label') || (f as HTMLElement).getAttribute('placeholder') || (f as HTMLInputElement).value || '')\n .filter((t) => t.trim().length > 0);\n return { title, content, fields };\n });\n\n if (dialogText && dialogText.content.length > 20) {\n result.modals.push({\n triggerElement: trigger.label,\n title: dialogText.title,\n content: dialogText.content,\n fields: dialogText.fields.slice(0, 10),\n });\n } else {\n // No modal appeared \u2014 check for accordion/disclosure panel reveals\n // Clicks on accordion triggers change aria-expanded but don't open a dialog.\n const accordionText = await page.evaluate(() => {\n const panels = Array.from(document.querySelectorAll(\n '[aria-expanded=\"true\"] + *, details[open] > :not(summary), [data-state=\"open\"]:not([role=\"dialog\"]):not([class*=\"modal\" i]), [data-headlessui-state=\"open\"] > *'\n )).filter((el) => {\n const s = window.getComputedStyle(el as HTMLElement);\n return s.display !== 'none' && s.visibility !== 'hidden';\n });\n if (panels.length === 0) return null;\n const panel = panels[0] as HTMLElement;\n const heading = panel.querySelector('h2,h3,h4')?.textContent?.replace(/\\s+/g, ' ').trim() || '';\n const content = panel.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) || '';\n return content.length > 15 ? { heading, content } : null;\n });\n if (accordionText) {\n result.modals.push({\n triggerElement: trigger.label,\n title: accordionText.heading || `${trigger.label} (revealed)`,\n content: accordionText.content,\n fields: [],\n });\n }\n }\n\n // Dismiss: Escape key first, then close button\n await page.keyboard.press('Escape');\n await page.waitForTimeout(400);\n\n // Check if toast appeared after the click\n const toasts = await collectToasts(page);\n for (const t of toasts) {\n if (t.length > 5) result.successMessages.push({ type: 'toast', content: t.slice(0, 200) });\n }\n } catch { /* this button's probe failed, try next */ }\n }\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 4: Select/radio \u2192 conditional reveals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n const conditionals = await probeConditionals(page);\n result.conditionals.push(...conditionals);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 5: Tab panels \u2014 click each tab, capture revealed content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.tabPanels = await probeTabPanels(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 6: Tooltips \u2014 hover/title/describedby capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.tooltips = await probeTooltips(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 7: Carousel/slider slides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.carouselSlides = await probeCarousels(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 8: Wizard/multi-step form steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.wizardSteps = await probeWizardSteps(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 9: Focus-trap detection on any open dialogs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.focusTraps = await detectFocusTraps(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 10: Skip navigation compliance (WCAG 2.4.1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.skipNavIssues = await probeSkipNavigation(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 11: Context menu capture on canvas/chart elements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.contextMenus = await probeContextMenus(page);\n } catch { /* non-fatal */ }\n\n return result;\n}\n\n/** Phase 5: Convert ProbeResult to typed ProbeObservationFact rows for direct DB storage. */\nexport function probeResultToObservations(result: ProbeResult): Array<{\n elementMeaning: string; observationType: string; observedText: string; triggeredBy: string;\n}> {\n const obs: Array<{ elementMeaning: string; observationType: string; observedText: string; triggeredBy: string }> = [];\n for (const ve of result.validationErrors) {\n obs.push({ elementMeaning: ve.field, observationType: 'VALIDATION_ERROR', observedText: ve.errorText, triggeredBy: ve.trigger });\n }\n for (const sm of result.successMessages) {\n obs.push({ elementMeaning: '(page)', observationType: 'SUCCESS_TOAST', observedText: sm.content, triggeredBy: sm.type });\n }\n for (const m of result.modals) {\n obs.push({ elementMeaning: m.triggerElement, observationType: 'MODAL', observedText: [m.title, m.content].filter(Boolean).join(' \u2014 ').slice(0, 500), triggeredBy: `button_click:${m.triggerElement}` });\n }\n for (const c of result.conditionals) {\n obs.push({ elementMeaning: c.triggerElement, observationType: 'CONDITIONAL_REVEAL', observedText: c.revealedText, triggeredBy: `select_change:${c.triggerElement}:${c.triggerValue}` });\n }\n for (const lp of result.loadingPatterns) {\n obs.push({ elementMeaning: '(submit button)', observationType: 'LOADING_STATE', observedText: lp, triggeredBy: 'form_submit' });\n }\n for (const tp of result.tabPanels ?? []) {\n obs.push({ elementMeaning: tp.tabLabel, observationType: 'TAB_PANEL', observedText: tp.panelContent, triggeredBy: `tab_click:${tp.tabLabel}` });\n }\n for (const tt of result.tooltips ?? []) {\n obs.push({ elementMeaning: tt.triggerLabel, observationType: 'TOOLTIP', observedText: tt.tooltipText, triggeredBy: 'hover_or_title' });\n }\n for (const cs of result.carouselSlides ?? []) {\n obs.push({ elementMeaning: `Slide ${cs.slideIndex + 1}`, observationType: 'CAROUSEL_SLIDE', observedText: cs.content, triggeredBy: `carousel_next:${cs.slideIndex}` });\n }\n for (const ws of result.wizardSteps ?? []) {\n obs.push({ elementMeaning: ws.stepLabel ?? `Step ${ws.stepIndex + 1}`, observationType: 'WIZARD_STEP', observedText: ws.content, triggeredBy: `wizard_next:${ws.stepIndex}` });\n }\n for (const ft of result.focusTraps ?? []) {\n obs.push({ elementMeaning: ft.modalTrigger, observationType: 'FOCUS_TRAP', observedText: ft.focusTrapped ? 'Focus correctly trapped inside dialog' : 'FAIL: focus escaped dialog \u2014 WCAG 2.1.2 violation', triggeredBy: 'tab_key_sequence' });\n }\n for (const issue of result.skipNavIssues ?? []) {\n obs.push({ elementMeaning: '(page)', observationType: 'SKIP_NAV_ISSUE', observedText: issue, triggeredBy: 'skip_nav_audit' });\n }\n for (const cm of result.contextMenus ?? []) {\n obs.push({ elementMeaning: cm.trigger, observationType: 'CONTEXT_MENU', observedText: cm.items.join(' | '), triggeredBy: 'right_click' });\n }\n return obs;\n}\n\n/**\n * Convert a ProbeResult into markdown to store as KnowledgeChunks\n * and update ERROR_CATALOGUE + INTERACTION_PATTERNS skill files.\n */\nexport function probeResultToMarkdown(result: ProbeResult): {\n errorCatalogueChunk: string;\n interactionPatternsChunk: string;\n} {\n const lines: string[] = [`## Probe Observations \u2014 ${result.url}\\n`];\n\n if (result.validationErrors.length > 0) {\n lines.push('### Validation Errors (observed)');\n for (const ve of result.validationErrors) {\n lines.push(`- **${ve.field}** (${ve.trigger}): \"${ve.errorText}\"`);\n }\n }\n\n if (result.successMessages.length > 0) {\n lines.push('\\n### Success / Feedback Messages (observed)');\n for (const sm of result.successMessages) {\n lines.push(`- ${sm.type}: \"${sm.content}\"`);\n }\n }\n\n if (result.modals.length > 0) {\n lines.push('\\n### Modal / Dialog Contents (observed)');\n for (const m of result.modals) {\n lines.push(`- **Trigger:** ${m.triggerElement}`);\n if (m.title) lines.push(` - Title: ${m.title}`);\n lines.push(` - Content: ${m.content.slice(0, 200)}`);\n if (m.fields.length > 0) lines.push(` - Fields: ${m.fields.join(', ')}`);\n }\n }\n\n if ((result.tabPanels ?? []).length > 0) {\n lines.push('\\n### Tab Panel Contents (observed)');\n for (const tp of result.tabPanels!) {\n lines.push(`- **Tab: ${tp.tabLabel}**${tp.panelHeading ? ` \u2014 ${tp.panelHeading}` : ''}: ${tp.panelContent.slice(0, 200)}`);\n }\n }\n\n if ((result.tooltips ?? []).length > 0) {\n lines.push('\\n### Tooltips (observed)');\n for (const tt of result.tooltips!) {\n lines.push(`- **${tt.triggerLabel}**: ${tt.tooltipText}`);\n }\n }\n\n if ((result.carouselSlides ?? []).length > 0) {\n lines.push('\\n### Carousel Slides (observed)');\n for (const cs of result.carouselSlides!) {\n lines.push(`- **Slide ${cs.slideIndex + 1}**${cs.heading ? ` \u2014 ${cs.heading}` : ''}: ${cs.content.slice(0, 200)}`);\n }\n }\n\n if ((result.wizardSteps ?? []).length > 0) {\n lines.push('\\n### Wizard / Multi-Step Form (observed)');\n for (const ws of result.wizardSteps!) {\n const label = ws.stepLabel ? ` \u2014 ${ws.stepLabel}` : '';\n lines.push(`- **Step ${ws.stepIndex + 1}${label}**: ${ws.content.slice(0, 200)}`);\n if (ws.fields.length > 0) lines.push(` - Fields: ${ws.fields.join(', ')}`);\n }\n }\n\n if ((result.focusTraps ?? []).length > 0) {\n lines.push('\\n### Focus-Trap Audit (observed)');\n for (const ft of result.focusTraps!) {\n const verdict = ft.focusTrapped ? '\u2705 PASS' : '\u274C FAIL (WCAG 2.1.2)';\n lines.push(`- **${ft.modalTrigger}**: ${verdict} \u2014 focused: [${ft.sampledFocused.slice(0, 4).join(', ')}]`);\n }\n }\n\n if (result.conditionals.length > 0) {\n lines.push('\\n### Conditional UI (observed)');\n for (const c of result.conditionals) {\n lines.push(`- **${c.triggerElement}** = \"${c.triggerValue}\" reveals: ${c.revealedText}`);\n }\n }\n\n if (result.loadingPatterns.length > 0) {\n lines.push('\\n### Loading State Text (observed)');\n for (const lp of result.loadingPatterns) lines.push(`- \"${lp}\"`);\n }\n\n const full = lines.join('\\n');\n\n const errorSection = result.validationErrors.length > 0\n ? `## Real Validation Errors \u2014 ${result.url}\\n` +\n result.validationErrors.map((e) => `- **${e.field}**: \"${e.errorText}\"`).join('\\n')\n : '';\n\n const interactionSection = [\n result.successMessages.length > 0\n ? `## Success Messages \u2014 ${result.url}\\n` + result.successMessages.map((s) => `- ${s.type}: \"${s.content}\"`).join('\\n')\n : '',\n result.loadingPatterns.length > 0\n ? `## Loading Patterns \u2014 ${result.url}\\n` + result.loadingPatterns.map((lp) => `- \"${lp}\"`).join('\\n')\n : '',\n result.modals.length > 0\n ? `## Modal Contents \u2014 ${result.url}\\n` + result.modals.map((m) => `### ${m.triggerElement}\\n${m.content}`).join('\\n')\n : '',\n ].filter(Boolean).join('\\n\\n');\n\n return {\n errorCatalogueChunk: errorSection,\n interactionPatternsChunk: interactionSection,\n };\n}\n\n/**\n * Run interactive probing for all screens discovered in a crawl session.\n * Called from crawlProject() when opts.interactive = true.\n */\nexport async function probeAllScreens(\n page: any,\n tenantId: string,\n projectId: string,\n discovered: Array<{ url: string; screenId?: string; elements?: any[] }>,\n onProgress?: (pct: number, step: string) => Promise<void>,\n): Promise<void> {\n const probeable = discovered.filter((d) => d.screenId && d.url && !d.url.includes('#'));\n if (probeable.length === 0) return;\n\n const errorCatalogueChunks: string[] = [];\n const interactionChunks: string[] = [];\n\n for (let i = 0; i < probeable.length; i++) {\n const screen = probeable[i];\n const pct = 88 + Math.round((i / probeable.length) * 10);\n await onProgress?.(pct, `Probing screen ${i + 1}/${probeable.length}: ${screen.url}`);\n\n try {\n // Navigate to screen\n if (page.url() !== screen.url) {\n await page.goto(screen.url, { waitUntil: 'domcontentloaded' });\n await page.waitForTimeout(1500); // SPA hydration\n }\n\n const result = await probeScreen(page, screen.screenId!, screen.url, screen.elements ?? []);\n\n // Phase 5: save structured probe observations (replaces per-re-probe)\n const observations = probeResultToObservations(result);\n if (observations.length > 0) {\n await AppBrain.saveProbeObservations(tenantId, projectId, screen.screenId!, observations).catch(console.warn);\n }\n\n // Store raw probe result as KnowledgeChunk (kept for BM25 / oversized projects)\n const md = probeResultToMarkdown(result);\n if (md.errorCatalogueChunk) errorCatalogueChunks.push(md.errorCatalogueChunk);\n if (md.interactionPatternsChunk) interactionChunks.push(md.interactionPatternsChunk);\n\n const probeMarkdown = [md.errorCatalogueChunk, md.interactionPatternsChunk].filter(Boolean).join('\\n\\n');\n if (probeMarkdown.length > 50) {\n await AppBrain.saveKnowledgeChunk(tenantId, projectId, 'probe', screen.screenId!, probeMarkdown, `Probe \u2014 ${screen.url}`).catch(console.warn);\n }\n\n if (result.validationErrors.length > 0) {\n console.log(`[probe] ${screen.url}: ${result.validationErrors.length} validation errors, ${result.modals.length} modals`);\n }\n } catch (err) {\n console.warn(`[probe] \u2717 ${screen.url}:`, err instanceof Error ? err.message : err);\n }\n }\n\n // Update ERROR_CATALOGUE skill file with all real observed errors\n if (errorCatalogueChunks.length > 0) {\n const combined = `# Error Catalogue (Real Observed \u2014 Interactive Probe)\\n\\n${errorCatalogueChunks.join('\\n\\n')}`;\n await AppBrain.upsertSkillFile(tenantId, projectId, 'ERROR_CATALOGUE', combined, {\n summary: `Real validation error messages observed via interactive probe across ${errorCatalogueChunks.length} screens`,\n scope: 'PROJECT',\n }).catch(console.warn);\n }\n\n // Update INTERACTION_PATTERNS skill file with real observed patterns\n if (interactionChunks.length > 0) {\n const combined = `# Interaction Patterns (Real Observed \u2014 Interactive Probe)\\n\\n${interactionChunks.join('\\n\\n')}`;\n await AppBrain.upsertSkillFile(tenantId, projectId, 'INTERACTION_PATTERNS', combined, {\n summary: `Real loading states, success messages, and modal contents observed via interactive probe`,\n scope: 'PROJECT',\n }).catch(console.warn);\n }\n\n await onProgress?.(98, 'Interactive probe complete');\n console.log(`[probe] Probed ${probeable.length} screens. Errors: ${errorCatalogueChunks.length}, Interactions: ${interactionChunks.length}`);\n}\n\n// \u2500\u2500 Keyboard Navigation Probe \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface FocusableElement {\n tag: string;\n role: string | null;\n ariaLabel: string | null;\n text: string | null;\n hasFocusIndicator: boolean;\n tabIndex: number | null;\n}\n\nexport interface KeyboardNavResult {\n focusableCount: number;\n elementsWithNoFocusIndicator: number;\n sequence: FocusableElement[];\n issues: string[];\n}\n\n/** Verify skip-navigation links exist and their targets are present in the DOM. */\nasync function probeSkipNavigation(page: Page): Promise<string[]> {\n const issues: string[] = [];\n try {\n const skipLinks = await page.evaluate(() =>\n Array.from(document.querySelectorAll('a[href^=\"#\"]'))\n .filter((el) => /skip|jump to|main content|content|nav/i.test((el as HTMLElement).innerText?.toLowerCase().trim() || ''))\n .map((el) => ({\n text: (el as HTMLElement).innerText?.trim() || '',\n href: el.getAttribute('href') || '',\n hasTarget: !!document.querySelector(el.getAttribute('href') || '#__invalid__'),\n }))\n );\n if (skipLinks.length === 0) {\n issues.push('No skip navigation links found (WCAG 2.4.1 \u2014 keyboard users cannot bypass nav blocks)');\n }\n for (const link of skipLinks) {\n if (!link.hasTarget) issues.push(`Skip link \"${link.text}\" \u2192 \"${link.href}\" target not found in DOM`);\n }\n } catch { /* non-fatal */ }\n return issues;\n}\n\n/** Right-click on canvas, chart, and context-menu elements to capture context menu items. */\nasync function probeContextMenus(page: Page): Promise<ContextMenuCapture[]> {\n const results: ContextMenuCapture[] = [];\n try {\n const targets = await page.$$('canvas, [data-contextmenu], [oncontextmenu], [class*=\"chart\" i]:not(script)');\n for (const t of targets.slice(0, 4)) {\n try {\n await t.click({ button: 'right', timeout: 1000 });\n await page.waitForTimeout(350);\n const items = await page.evaluate(() =>\n Array.from(document.querySelectorAll('[role=\"menu\"] [role=\"menuitem\"], .context-menu-item, [class*=\"contextmenu\" i] li'))\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter(Boolean).slice(0, 12)\n );\n if (items.length > 0) {\n const trigger = await t.evaluate((el) => (el as HTMLElement).getAttribute('aria-label') || (el as HTMLElement).className.slice(0, 40) || el.tagName).catch(() => '');\n results.push({ trigger, items });\n }\n await page.keyboard.press('Escape').catch(() => { });\n } catch { /* skip this target */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Click carousel next buttons up to 8 slides and record content of each revealed slide. */\nasync function probeCarousels(page: Page): Promise<CarouselSlide[]> {\n const results: CarouselSlide[] = [];\n try {\n const CAROUSEL_SEL = '[class*=\"carousel\" i], [class*=\"slider\" i], [class*=\"swiper\" i], [role=\"region\"][aria-roledescription*=\"carousel\" i]';\n const hasCarousel = await page.evaluate((sel) => !!document.querySelector(sel), CAROUSEL_SEL);\n if (!hasCarousel) return results;\n\n const NEXT_SEL = '[aria-label*=\"next\" i], [aria-label*=\"Next\"], [class*=\"next\" i]:not([class*=\"footnext\"]):not(a), button[class*=\"arrow-right\" i], button[class*=\"chevron-right\" i]';\n\n // Capture slide 0 (current state)\n const slide0 = await page.evaluate((carSel) => {\n const car = document.querySelector(carSel) as HTMLElement | null;\n if (!car) return null;\n const active = car.querySelector('[aria-hidden=\"false\"], .active, [class*=\"active\" i], [class*=\"current\" i]') as HTMLElement | null;\n const target = active ?? car;\n return { heading: target.querySelector('h2,h3,h4')?.textContent?.trim() ?? '', content: target.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 300) ?? '' };\n }, CAROUSEL_SEL);\n if (slide0?.content && slide0.content.length > 15) results.push({ slideIndex: 0, heading: slide0.heading || undefined, content: slide0.content });\n\n const seen = new Set<string>(slide0?.content ? [slide0.content] : []);\n for (let i = 1; i < 8; i++) {\n try {\n const nextBtn = page.locator(NEXT_SEL).first();\n const visible = await nextBtn.isVisible().catch(() => false);\n if (!visible) break;\n await nextBtn.click({ timeout: 1000 });\n await page.waitForTimeout(600);\n const slide = await page.evaluate((carSel) => {\n const car = document.querySelector(carSel) as HTMLElement | null;\n if (!car) return null;\n const active = car.querySelector('[aria-hidden=\"false\"], .active, [class*=\"active\" i], [class*=\"current\" i]') as HTMLElement | null;\n const target = active ?? car;\n return { heading: target.querySelector('h2,h3,h4')?.textContent?.trim() ?? '', content: target.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 300) ?? '' };\n }, CAROUSEL_SEL);\n if (!slide?.content || slide.content.length < 15 || seen.has(slide.content)) break;\n seen.add(slide.content);\n results.push({ slideIndex: i, heading: slide.heading || undefined, content: slide.content });\n } catch { break; }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Follow Next/Continue buttons to capture each wizard step's fields and content. */\nasync function probeWizardSteps(page: Page): Promise<WizardStep[]> {\n const results: WizardStep[] = [];\n try {\n const NEXT_LABELS = /^(next|continue|proceed|step \\d|\u203A|\u2192|forward)/i;\n const BACK_LABELS = /^(back|previous|prev|\u2039|\u2190)/i;\n const WIZARD_INDICATORS = '[role=\"progressbar\"], [class*=\"stepper\" i], [class*=\"wizard\" i], [class*=\"progress-step\" i], [aria-label*=\"step\" i]';\n const isWizard = await page.evaluate((sel) => !!document.querySelector(sel), WIZARD_INDICATORS);\n if (!isWizard) return results;\n\n const captureStep = async (stepIndex: number): Promise<WizardStep> => {\n const data = await page.evaluate(() => {\n const content = (document.querySelector('main, [role=\"main\"], form') as HTMLElement | null)?.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) ?? '';\n const stepLabel = document.querySelector('[aria-current=\"step\"], [class*=\"step-label\" i], [class*=\"stepper-label\" i]')?.textContent?.trim() ?? '';\n const fields = Array.from(document.querySelectorAll('input:not([type=\"hidden\"]), select, textarea'))\n .map((el) => (el as HTMLElement).getAttribute('aria-label') || (el as HTMLInputElement).placeholder || (el as HTMLInputElement).name || '')\n .filter(Boolean).slice(0, 10);\n return { content, stepLabel, fields };\n });\n return { stepIndex, stepLabel: data.stepLabel || undefined, content: data.content, fields: data.fields };\n };\n\n results.push(await captureStep(0));\n\n for (let i = 1; i < 6; i++) {\n try {\n const nextLabel = await page.evaluate((nextRe) => {\n const btns = Array.from(document.querySelectorAll('button, [role=\"button\"]'));\n const btn = btns.find((b) => new RegExp(nextRe).test((b as HTMLElement).innerText?.trim() || ''));\n return (btn as HTMLElement)?.innerText?.trim() || null;\n }, NEXT_LABELS.source);\n if (!nextLabel) break;\n const btn = page.locator(`button:has-text(\"${nextLabel}\")`).first();\n await btn.click({ timeout: 1500 });\n await page.waitForTimeout(800);\n results.push(await captureStep(i));\n } catch { break; }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** For each open modal, Tab 8\u00D7 and verify focus stays inside the dialog (focus-trap check). */\nasync function detectFocusTraps(page: Page): Promise<FocusTrapResult[]> {\n const results: FocusTrapResult[] = [];\n try {\n const openDialogs = await page.evaluate(() => {\n return Array.from(document.querySelectorAll('[role=\"dialog\"], [role=\"alertdialog\"]'))\n .filter((el) => window.getComputedStyle(el).display !== 'none')\n .map((el) => el.getAttribute('aria-label') || el.querySelector('[role=\"heading\"]')?.textContent?.trim() || 'dialog');\n });\n for (const dialogLabel of openDialogs.slice(0, 3)) {\n const sampled: string[] = [];\n let allInside = true;\n for (let i = 0; i < 8; i++) {\n await page.keyboard.press('Tab');\n await page.waitForTimeout(80);\n const focused = await page.evaluate(() => {\n const el = document.activeElement as HTMLElement | null;\n const inDialog = !!el?.closest('[role=\"dialog\"], [role=\"alertdialog\"]');\n return { label: el?.getAttribute('aria-label') || el?.textContent?.trim().slice(0, 40) || el?.tagName || '', inDialog };\n });\n sampled.push(focused.label);\n if (!focused.inDialog) { allInside = false; break; }\n }\n results.push({ modalTrigger: dialogLabel, focusTrapped: allInside, sampledFocused: sampled });\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Click each visible tab and record the panel content revealed. */\nasync function probeTabPanels(page: Page): Promise<TabPanelCapture[]> {\n const results: TabPanelCapture[] = [];\n try {\n const tabs = await page.$$('[role=\"tab\"]:not([aria-disabled=\"true\"])');\n for (const tab of tabs.slice(0, 10)) {\n try {\n const tabLabel = await tab.evaluate((el) => (el as HTMLElement).innerText?.trim() || el.getAttribute('aria-label') || '');\n if (!tabLabel) continue;\n await tab.click({ timeout: 1000 });\n await page.waitForTimeout(400);\n const panelContent = await page.evaluate(() => {\n const panel = document.querySelector('[role=\"tabpanel\"]:not([hidden])') as HTMLElement | null;\n if (!panel) return null;\n const style = window.getComputedStyle(panel);\n if (style.display === 'none' || style.visibility === 'hidden') return null;\n return {\n heading: panel.querySelector('h2,h3,h4')?.textContent?.trim() ?? '',\n content: panel.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) ?? '',\n };\n });\n if (panelContent?.content && panelContent.content.length > 15) {\n results.push({ tabLabel: tabLabel.slice(0, 60), panelContent: panelContent.content, panelHeading: panelContent.heading || undefined });\n }\n } catch { /* this tab failed, try next */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Hover elements with title/aria-describedby to capture tooltip text. */\nasync function probeTooltips(page: Page): Promise<TooltipCapture[]> {\n const results: TooltipCapture[] = [];\n try {\n const candidates = await page.evaluate(() => {\n return Array.from(document.querySelectorAll('[title], [aria-describedby], [data-tooltip], [data-tip], [class*=\"tooltip\" i]'))\n .filter((el) => {\n const style = window.getComputedStyle(el as HTMLElement);\n return style.display !== 'none' && style.visibility !== 'hidden';\n })\n .slice(0, 8)\n .map((el) => ({\n label: (el as HTMLElement).innerText?.trim().slice(0, 60) || el.getAttribute('aria-label') || '',\n title: el.getAttribute('title') || '',\n describedBy: el.getAttribute('aria-describedby') || '',\n selector: el.tagName.toLowerCase() + (el.id ? `#${el.id}` : ''),\n }));\n });\n for (const c of candidates) {\n try {\n // Prefer title attribute directly \u2014 no need to hover for static tooltips\n if (c.title && c.title.length > 5) {\n results.push({ triggerLabel: c.label || c.selector, tooltipText: c.title.slice(0, 200) });\n continue;\n }\n if (c.describedBy) {\n const tooltipText = await page.evaluate((id) => document.getElementById(id)?.textContent?.trim() ?? '', c.describedBy);\n if (tooltipText && tooltipText.length > 5) {\n results.push({ triggerLabel: c.label || c.selector, tooltipText: tooltipText.slice(0, 200) });\n }\n }\n } catch { /* skip this element */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/**\n * Probe keyboard navigation accessibility by pressing Tab up to 50 times and\n * recording each focused element's properties and focus-indicator visibility.\n * Stops early if focus wraps back to the start or hits the same element twice.\n */\nexport async function probeKeyboardNav(page: Page): Promise<KeyboardNavResult> {\n const sequence: FocusableElement[] = [];\n const issues: string[] = [];\n const seenKeys = new Set<string>();\n let firstKey: string | null = null;\n\n for (let i = 0; i < 50; i++) {\n await page.keyboard.press('Tab');\n\n const el = await page.evaluate(() => {\n const active = document.activeElement as HTMLElement | null;\n if (!active || active === document.body) return null;\n const outline = window.getComputedStyle(active).outline;\n return {\n tag: active.tagName.toLowerCase(),\n role: active.getAttribute('role'),\n ariaLabel: active.getAttribute('aria-label'),\n text: (active.innerText ?? '').trim().slice(0, 60) || null,\n outline,\n tabIndex: active.tabIndex ?? null,\n };\n });\n\n if (!el) break;\n\n const hasFocusIndicator =\n el.outline !== 'none' &&\n el.outline !== '' &&\n !el.outline.startsWith('0px') &&\n !el.outline.startsWith('rgba(0, 0, 0, 0)') &&\n !el.outline.startsWith('rgba(0,0,0,0)');\n\n const focusable: FocusableElement = {\n tag: el.tag,\n role: el.role,\n ariaLabel: el.ariaLabel,\n text: el.text,\n hasFocusIndicator,\n tabIndex: el.tabIndex,\n };\n\n const key = `${el.tag}|${el.text ?? ''}|${el.role ?? ''}`;\n\n if (i === 0) {\n firstKey = key;\n } else if (key === firstKey || seenKeys.has(key)) {\n // Focus wrapped back to start or hit a duplicate \u2014 stop probing\n break;\n }\n\n seenKeys.add(key);\n sequence.push(focusable);\n\n if (!hasFocusIndicator) {\n issues.push(`Element #${i + 1} (${el.tag}) has no visible focus indicator`);\n }\n }\n\n return {\n focusableCount: sequence.length,\n elementsWithNoFocusIndicator: issues.length,\n sequence,\n issues,\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Interactive Probe \u2014 Phase 2 Dynamic Observation\n *\n * The static crawl captures DOM structure at rest. This module runs AFTER the static crawl\n * to observe dynamic behaviour by actually interacting with the UI:\n *\n * 1. Empty submit \u2192 captures required-field validation error messages\n * 2. Invalid format \u2192 captures format validation errors (bad email, short password)\n * 3. Button clicks \u2192 captures modal/dialog contents\n * 4. Select changes \u2192 captures conditional reveals (show/hide logic)\n * 5. Success paths \u2192 captures toast/banner messages (where input is safe)\n *\n * Results are stored as KnowledgeChunks (sourceType='probe') and update the\n * ERROR_CATALOGUE + INTERACTION_PATTERNS skill files with real observed data\n * instead of LLM inference.\n *\n * Call signature: probeScreen(page, screenId, elements, opts)\n * Called from crawlProject() after static element extraction when opts.interactive=true.\n */\n\nimport type { Page } from 'playwright';\nimport { AppBrain } from '@detiq/app-brain';\n\nexport interface ProbeElementFact {\n meaning: string;\n role: string;\n expectedData?: string;\n}\n\nexport interface ValidationError {\n field: string;\n trigger: 'empty_submit' | 'invalid_format' | 'boundary';\n errorText: string;\n}\n\nexport interface SuccessMessage {\n type: 'toast' | 'inline_success' | 'redirect';\n content: string;\n}\n\nexport interface ModalObservation {\n triggerElement: string;\n title: string;\n content: string;\n fields: string[];\n stateId?: string;\n domSnapshot?: string;\n}\n\nexport interface ConditionalReveal {\n triggerElement: string;\n triggerValue: string;\n revealedText: string;\n stateId?: string;\n domSnapshot?: string;\n}\n\nexport interface TooltipCapture {\n triggerLabel: string;\n tooltipText: string;\n}\n\nexport interface TabPanelCapture {\n tabLabel: string;\n panelContent: string;\n panelHeading?: string;\n}\n\nexport interface CarouselSlide {\n slideIndex: number;\n heading?: string;\n content: string;\n}\n\nexport interface WizardStep {\n stepIndex: number;\n stepLabel?: string;\n content: string;\n fields: string[];\n}\n\nexport interface FocusTrapResult {\n modalTrigger: string;\n focusTrapped: boolean;\n sampledFocused: string[];\n}\n\nexport interface ContextMenuCapture {\n trigger: string;\n items: string[];\n}\n\nexport interface ProbeResult {\n screenId: string;\n url: string;\n validationErrors: ValidationError[];\n successMessages: SuccessMessage[];\n modals: ModalObservation[];\n conditionals: ConditionalReveal[];\n loadingPatterns: string[];\n tooltips?: TooltipCapture[];\n tabPanels?: TabPanelCapture[];\n carouselSlides?: CarouselSlide[];\n wizardSteps?: WizardStep[];\n focusTraps?: FocusTrapResult[];\n skipNavIssues?: string[];\n contextMenus?: ContextMenuCapture[];\n}\n\n// \u2500\u2500 CSS selectors that reliably find error messages across frameworks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst ERROR_SELECTORS = [\n '[role=\"alert\"]',\n '[aria-live=\"assertive\"]',\n '[aria-live=\"polite\"]',\n '.invalid-feedback',\n '.field-error',\n '.error-message',\n '.form-error',\n '.validation-error',\n '[class*=\"error\" i]',\n '[class*=\"invalid\" i]',\n '[class*=\"Error\"]',\n 'p.text-red-500', 'p.text-red-600', 'p.text-destructive', 'p.text-danger',\n '[data-error]', '[data-invalid]',\n '.ant-form-item-explain-error', // Ant Design\n '.el-form-item__error', // Element UI\n '.chakra-form__error-message', // Chakra UI\n '.mantine-InputWrapper-error', // Mantine\n].join(', ');\n\n// \u2500\u2500 Toast / notification selectors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst TOAST_SELECTORS = [\n '[role=\"status\"]',\n '[data-sonner-toast]',\n '[data-toast]',\n '[data-hot-toast]',\n '.Toastify__toast',\n '.toast-message',\n '.notification-body',\n '.notistack-snackbar',\n '[class*=\"toast\" i]',\n '[class*=\"Toast\"]',\n '[class*=\"snackbar\" i]',\n '[class*=\"notification\" i]',\n].join(', ');\n\n/** Wait up to `maxMs` for any selector to appear, then return its text content. */\nasync function waitForAny(page: Page, selector: string, maxMs = 2500): Promise<string[]> {\n try {\n await page.waitForSelector(selector, { timeout: maxMs, state: 'visible' });\n } catch { return []; }\n return page.$$eval(selector, (els) =>\n els.map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim()).filter(Boolean)\n );\n}\n\n/** Collect all currently-visible error messages. */\nasync function collectErrors(page: Page): Promise<string[]> {\n try {\n return page.$$eval(ERROR_SELECTORS, (els) =>\n els\n .filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden' && (el as HTMLElement).innerText?.trim().length > 0;\n })\n .map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim())\n .filter(Boolean)\n );\n } catch { return []; }\n}\n\n/** Collect any visible toast / notification content. */\nasync function collectToasts(page: Page): Promise<string[]> {\n try {\n return page.$$eval(TOAST_SELECTORS, (els) =>\n els\n .filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && (el as HTMLElement).innerText?.trim().length > 0;\n })\n .map((el) => (el as HTMLElement).innerText?.replace(/\\s+/g, ' ').trim())\n .filter(Boolean)\n );\n } catch { return []; }\n}\n\n/** Find the primary submit button on the page. */\nasync function findSubmitButton(page: Page): Promise<string | null> {\n return page.evaluate(() => {\n const SUBMIT_KW = /submit|send|save|create|confirm|continue|proceed|register|sign.?in|log.?in|apply|verify|search|next|finish|complete|order|buy|checkout/i;\n const candidates = Array.from(document.querySelectorAll('button, [role=\"button\"], input[type=\"submit\"]')) as HTMLElement[];\n const primary = candidates.find((el) => {\n const style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') return false;\n const text = el.innerText || (el as HTMLInputElement).value || el.getAttribute('aria-label') || '';\n return SUBMIT_KW.test(text);\n });\n if (!primary) return null;\n return primary.getAttribute('data-testid') || primary.getAttribute('aria-label') || primary.innerText?.trim().slice(0, 60);\n });\n}\n\n/** Find modal-trigger buttons and clickable row-level items (e.g. test-case rows opening side panels). */\nasync function findModalTriggers(page: Page): Promise<Array<{ selector: string; label: string }>> {\n return page.evaluate(() => {\n const SKIP = /submit|send|save|create|log.?in|sign.?in|register|continue|checkout|buy|order|search|close|cancel|dismiss/i;\n const NAV_PARENTS = new Set(['NAV', 'HEADER', 'FOOTER']);\n\n function isVisible(el: Element): boolean {\n const s = window.getComputedStyle(el as HTMLElement);\n return s.display !== 'none' && s.visibility !== 'hidden' && (s.opacity !== '0');\n }\n\n const seen = new Set<string>();\n const results: Array<{ selector: string; label: string }> = [];\n\n // Standard button / role=button elements\n for (const el of Array.from(document.querySelectorAll('button, [role=\"button\"]'))) {\n if (!isVisible(el)) continue;\n const label = ((el as HTMLElement).innerText?.trim() || el.getAttribute('aria-label') || '').slice(0, 60);\n if (!label || SKIP.test(label) || seen.has(label)) continue;\n let p = el.parentElement; let inNav = false;\n while (p) { if (NAV_PARENTS.has(p.tagName)) { inNav = true; break; } p = p.parentElement; }\n if (inNav) continue;\n seen.add(label);\n results.push({ selector: `button:has-text(\"${label.slice(0, 40)}\")`, label });\n if (results.length >= 15) break;\n }\n\n // Row-level clickable elements \u2014 table/list rows that open side panels on click\n // (e.g. test-case rows \u2192 ScreenPreviewPanel, ScriptPopup)\n const rowCandidates = Array.from(document.querySelectorAll(\n '[role=\"row\"][tabindex=\"0\"], tr[tabindex=\"0\"], [role=\"listitem\"][tabindex=\"0\"], [class*=\"list-item\"][tabindex=\"0\"], [class*=\"table-row\"][tabindex=\"0\"]'\n )).filter(isVisible).slice(0, 3);\n for (const row of rowCandidates) {\n const ariaLabel = row.getAttribute('aria-label');\n const textLabel = (row as HTMLElement).innerText?.trim().split('\\n')[0].trim().slice(0, 60);\n const label = (ariaLabel || textLabel || '').slice(0, 60);\n if (!label || seen.has(label)) continue;\n seen.add(label);\n // Use aria-label selector when available; fall back to row role + text content\n const selector = ariaLabel\n ? `[aria-label=\"${ariaLabel}\"]`\n : `[role=\"row\"]:has-text(\"${label.slice(0, 40)}\")`;\n results.push({ selector, label });\n }\n\n return results.slice(0, 20);\n });\n}\n\n/** Try to interact with select elements and observe conditional reveals. */\nasync function probeConditionals(page: Page): Promise<ConditionalReveal[]> {\n const results: ConditionalReveal[] = [];\n try {\n const selects = await page.$$('select');\n for (const sel of selects.slice(0, 5)) {\n const label = await sel.getAttribute('aria-label') || await sel.getAttribute('name') || 'select';\n const options = await sel.$$eval('option', (opts) => opts.slice(1, 3).map((o) => ({ value: o.value, text: o.textContent?.trim() })));\n for (const opt of options) {\n if (!opt.value) continue;\n const domBefore = await page.evaluate(() => document.body.innerText.length);\n await sel.selectOption(opt.value);\n await page.waitForTimeout(500);\n const domAfter = await page.evaluate(() => document.body.innerText.length);\n if (Math.abs(domAfter - domBefore) > 50) {\n const revealed = await page.evaluate(() => {\n // Grab text from any newly-visible sections\n return Array.from(document.querySelectorAll('[data-conditional],[data-show],[class*=\"conditional\"]'))\n .filter((el) => window.getComputedStyle(el).display !== 'none')\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter((t) => t && t.length > 10)\n .slice(0, 3)\n .join(' | ');\n });\n if (revealed) {\n const domSnapshot = await page.content();\n const stateId = await page.evaluate(() => {\n let hash = 0;\n const str = document.body.innerText;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) - hash) + str.charCodeAt(i);\n hash = hash & hash;\n }\n return Math.abs(hash).toString(16);\n });\n results.push({ triggerElement: label, triggerValue: opt.text || opt.value, revealedText: revealed.slice(0, 200), stateId, domSnapshot });\n }\n }\n }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/**\n * Run interactive probing on a single screen page.\n * The page must already be navigated to the screen URL.\n * `elements` is the output of the static extractElements pass.\n */\nexport async function probeScreen(\n page: Page,\n screenId: string,\n url: string,\n elements: ProbeElementFact[],\n): Promise<ProbeResult> {\n const result: ProbeResult = {\n screenId, url,\n validationErrors: [], successMessages: [], modals: [], conditionals: [], loadingPatterns: [],\n };\n\n const formInputs = elements.filter((e) =>\n /input|text|email|password|tel|search|textarea|select/i.test(e.role)\n );\n const hasForm = formInputs.length > 0;\n\n // \u2500\u2500 Probe 1: Empty submit \u2192 capture required-field errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (hasForm) {\n try {\n const submitLabel = await findSubmitButton(page);\n if (submitLabel) {\n // Click submit with no data filled in\n const submitBtn = page.locator(`button:has-text(\"${submitLabel}\")`).first();\n await submitBtn.click({ timeout: 2000 });\n await page.waitForTimeout(1500);\n\n const errors = await collectErrors(page);\n // Prefer DOM-proximity mapping via aria-invalid + aria-describedby\n const ariaFieldErrors = await page.evaluate(() =>\n Array.from(document.querySelectorAll('[aria-invalid=\"true\"]')).map((el) => {\n const describedBy = el.getAttribute('aria-describedby');\n const errorEl = describedBy ? document.getElementById(describedBy) : null;\n const label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || (el as HTMLInputElement).name || '';\n return { label, errorText: errorEl?.textContent?.trim() || '' };\n }).filter((f) => f.errorText)\n );\n const ariaMatched = new Set<string>();\n for (const af of ariaFieldErrors) {\n ariaMatched.add(af.errorText);\n result.validationErrors.push({ field: af.label || 'unknown', trigger: 'empty_submit', errorText: af.errorText.slice(0, 150) });\n }\n // Fallback: text-proximity match for errors not covered by aria-invalid\n for (const errorText of errors.filter((e) => !ariaMatched.has(e))) {\n const matchedField = formInputs.find((el) =>\n errorText.toLowerCase().includes(el.meaning.toLowerCase().split(' ')[0]) ||\n el.meaning.toLowerCase().includes(errorText.toLowerCase().split(' ')[0])\n );\n result.validationErrors.push({\n field: matchedField?.meaning ?? 'unknown',\n trigger: 'empty_submit',\n errorText: errorText.slice(0, 150),\n });\n }\n\n // Check for loading state text during submit\n const loadingText = await page.evaluate(() => {\n const loading = Array.from(document.querySelectorAll('button, [aria-busy=\"true\"], [class*=\"loading\" i], [class*=\"spinner\" i]'))\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter((t) => t && /loading|saving|submitting|sending|processing/i.test(t));\n return loading.slice(0, 3);\n });\n result.loadingPatterns.push(...loadingText);\n }\n } catch { /* non-fatal \u2014 form probe failed, continue */ }\n }\n\n // \u2500\u2500 Probe 2: Invalid format inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (hasForm) {\n try {\n const emailField = formInputs.find((e) => e.expectedData === 'valid_email' || /email/i.test(e.meaning));\n const passwordField = formInputs.find((e) => e.expectedData === 'password' || /password/i.test(e.meaning));\n\n if (emailField) {\n const emailLocator = page.locator('input[type=\"email\"], input[name*=\"email\"], input[placeholder*=\"email\" i]').first();\n try {\n await emailLocator.fill('notanemail', { timeout: 1000 });\n await emailLocator.blur();\n await page.waitForTimeout(600);\n const errors = await collectErrors(page);\n for (const errorText of errors) {\n result.validationErrors.push({ field: emailField.meaning, trigger: 'invalid_format', errorText: errorText.slice(0, 150) });\n }\n } catch { /* locator not found */ }\n }\n\n if (passwordField) {\n const pwLocator = page.locator('input[type=\"password\"]').first();\n try {\n await pwLocator.fill('123', { timeout: 1000 });\n await pwLocator.blur();\n await page.waitForTimeout(600);\n const errors = await collectErrors(page);\n for (const errorText of errors) {\n if (!result.validationErrors.some((e) => e.errorText === errorText)) {\n result.validationErrors.push({ field: passwordField.meaning, trigger: 'invalid_format', errorText: errorText.slice(0, 150) });\n }\n }\n } catch { /* locator not found */ }\n }\n } catch { /* non-fatal */ }\n }\n\n // \u2500\u2500 Probe 3: Button clicks \u2192 discover modal/dialog contents \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n const triggers = await findModalTriggers(page);\n for (const trigger of triggers) {\n try {\n const btn = page.locator(trigger.selector).first();\n const domBefore = await page.evaluate(() => document.body.innerText.length);\n\n await btn.click({ timeout: 1500 });\n // Adaptive wait: some triggers launch async jobs (e.g. script generation 5\u201330s).\n // Wait for a visible dialog/panel rather than a fixed sleep.\n try {\n await page.waitForSelector(\n '[role=\"dialog\"],[role=\"alertdialog\"],[aria-modal=\"true\"],[data-state=\"open\"],[class*=\"modal\" i]:not([class*=\"backdrop\" i]),[class*=\"Dialog\"]:not([class*=\"Overlay\"])',\n { timeout: 8000, state: 'visible' }\n );\n } catch {\n // No dialog appeared \u2014 wait briefly for accordion/panel DOM settle\n await page.waitForTimeout(300);\n }\n\n // Check if a dialog/modal appeared\n const dialogText = await page.evaluate(() => {\n const DIALOG_SEL = '[role=\"dialog\"], [role=\"alertdialog\"], .modal, .Modal, [class*=\"modal\" i], [class*=\"dialog\" i], [class*=\"Dialog\"]';\n const dialogs = Array.from(document.querySelectorAll(DIALOG_SEL))\n .filter((el) => window.getComputedStyle(el).display !== 'none');\n if (dialogs.length === 0) return null;\n const d = dialogs[0] as HTMLElement;\n const title = d.querySelector('[role=\"heading\"], h1, h2, h3')?.textContent?.trim() || '';\n const content = d.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400);\n const fields = Array.from(d.querySelectorAll('input, select, textarea, button'))\n .map((f) => (f as HTMLElement).getAttribute('aria-label') || (f as HTMLElement).getAttribute('placeholder') || (f as HTMLInputElement).value || '')\n .filter((t) => t.trim().length > 0);\n return { title, content, fields };\n });\n\n const domSnapshot = await page.content();\n const stateId = await page.evaluate(() => {\n // simple simHash placeholder\n let hash = 0;\n const str = document.body.innerText;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) - hash) + str.charCodeAt(i);\n hash = hash & hash;\n }\n return Math.abs(hash).toString(16);\n });\n\n if (dialogText && dialogText.content.length > 20) {\n result.modals.push({\n triggerElement: trigger.label,\n title: dialogText.title,\n content: dialogText.content,\n fields: dialogText.fields.slice(0, 10),\n stateId,\n domSnapshot,\n });\n } else {\n // No modal appeared \u2014 check for accordion/disclosure panel reveals\n // Clicks on accordion triggers change aria-expanded but don't open a dialog.\n const accordionText = await page.evaluate(() => {\n const panels = Array.from(document.querySelectorAll(\n '[aria-expanded=\"true\"] + *, details[open] > :not(summary), [data-state=\"open\"]:not([role=\"dialog\"]):not([class*=\"modal\" i]), [data-headlessui-state=\"open\"] > *'\n )).filter((el) => {\n const s = window.getComputedStyle(el as HTMLElement);\n return s.display !== 'none' && s.visibility !== 'hidden';\n });\n if (panels.length === 0) return null;\n const panel = panels[0] as HTMLElement;\n const heading = panel.querySelector('h2,h3,h4')?.textContent?.replace(/\\s+/g, ' ').trim() || '';\n const content = panel.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) || '';\n return content.length > 15 ? { heading, content } : null;\n });\n if (accordionText) {\n result.modals.push({\n triggerElement: trigger.label,\n title: accordionText.heading || `${trigger.label} (revealed)`,\n content: accordionText.content,\n fields: [],\n stateId,\n domSnapshot,\n });\n }\n }\n\n // Dismiss: Escape key first, then close button\n await page.keyboard.press('Escape');\n await page.waitForTimeout(400);\n\n // Check if toast appeared after the click\n const toasts = await collectToasts(page);\n for (const t of toasts) {\n if (t.length > 5) result.successMessages.push({ type: 'toast', content: t.slice(0, 200) });\n }\n } catch { /* this button's probe failed, try next */ }\n }\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 4: Select/radio \u2192 conditional reveals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n const conditionals = await probeConditionals(page);\n result.conditionals.push(...conditionals);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 5: Tab panels \u2014 click each tab, capture revealed content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.tabPanels = await probeTabPanels(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 6: Tooltips \u2014 hover/title/describedby capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.tooltips = await probeTooltips(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 7: Carousel/slider slides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.carouselSlides = await probeCarousels(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 8: Wizard/multi-step form steps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.wizardSteps = await probeWizardSteps(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 9: Focus-trap detection on any open dialogs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.focusTraps = await detectFocusTraps(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 10: Skip navigation compliance (WCAG 2.4.1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.skipNavIssues = await probeSkipNavigation(page);\n } catch { /* non-fatal */ }\n\n // \u2500\u2500 Probe 11: Context menu capture on canvas/chart elements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n result.contextMenus = await probeContextMenus(page);\n } catch { /* non-fatal */ }\n\n return result;\n}\n\n/** Phase 5: Convert ProbeResult to typed ProbeObservationFact rows for direct DB storage. */\nexport function probeResultToObservations(result: ProbeResult): Array<{\n elementMeaning: string; observationType: string; observedText: string; triggeredBy: string;\n}> {\n const obs: Array<{ elementMeaning: string; observationType: string; observedText: string; triggeredBy: string }> = [];\n for (const ve of result.validationErrors) {\n obs.push({ elementMeaning: ve.field, observationType: 'VALIDATION_ERROR', observedText: ve.errorText, triggeredBy: ve.trigger });\n }\n for (const sm of result.successMessages) {\n obs.push({ elementMeaning: '(page)', observationType: 'SUCCESS_TOAST', observedText: sm.content, triggeredBy: sm.type });\n }\n for (const m of result.modals) {\n obs.push({ elementMeaning: m.triggerElement, observationType: 'MODAL', observedText: [m.title, m.content].filter(Boolean).join(' \u2014 ').slice(0, 500), triggeredBy: `button_click:${m.triggerElement}` });\n }\n for (const c of result.conditionals) {\n obs.push({ elementMeaning: c.triggerElement, observationType: 'CONDITIONAL_REVEAL', observedText: c.revealedText, triggeredBy: `select_change:${c.triggerElement}:${c.triggerValue}` });\n }\n for (const lp of result.loadingPatterns) {\n obs.push({ elementMeaning: '(submit button)', observationType: 'LOADING_STATE', observedText: lp, triggeredBy: 'form_submit' });\n }\n for (const tp of result.tabPanels ?? []) {\n obs.push({ elementMeaning: tp.tabLabel, observationType: 'TAB_PANEL', observedText: tp.panelContent, triggeredBy: `tab_click:${tp.tabLabel}` });\n }\n for (const tt of result.tooltips ?? []) {\n obs.push({ elementMeaning: tt.triggerLabel, observationType: 'TOOLTIP', observedText: tt.tooltipText, triggeredBy: 'hover_or_title' });\n }\n for (const cs of result.carouselSlides ?? []) {\n obs.push({ elementMeaning: `Slide ${cs.slideIndex + 1}`, observationType: 'CAROUSEL_SLIDE', observedText: cs.content, triggeredBy: `carousel_next:${cs.slideIndex}` });\n }\n for (const ws of result.wizardSteps ?? []) {\n obs.push({ elementMeaning: ws.stepLabel ?? `Step ${ws.stepIndex + 1}`, observationType: 'WIZARD_STEP', observedText: ws.content, triggeredBy: `wizard_next:${ws.stepIndex}` });\n }\n for (const ft of result.focusTraps ?? []) {\n obs.push({ elementMeaning: ft.modalTrigger, observationType: 'FOCUS_TRAP', observedText: ft.focusTrapped ? 'Focus correctly trapped inside dialog' : 'FAIL: focus escaped dialog \u2014 WCAG 2.1.2 violation', triggeredBy: 'tab_key_sequence' });\n }\n for (const issue of result.skipNavIssues ?? []) {\n obs.push({ elementMeaning: '(page)', observationType: 'SKIP_NAV_ISSUE', observedText: issue, triggeredBy: 'skip_nav_audit' });\n }\n for (const cm of result.contextMenus ?? []) {\n obs.push({ elementMeaning: cm.trigger, observationType: 'CONTEXT_MENU', observedText: cm.items.join(' | '), triggeredBy: 'right_click' });\n }\n return obs;\n}\n\n/**\n * Convert a ProbeResult into markdown to store as KnowledgeChunks\n * and update ERROR_CATALOGUE + INTERACTION_PATTERNS skill files.\n */\nexport function probeResultToMarkdown(result: ProbeResult): {\n errorCatalogueChunk: string;\n interactionPatternsChunk: string;\n} {\n const lines: string[] = [`## Probe Observations \u2014 ${result.url}\\n`];\n\n if (result.validationErrors.length > 0) {\n lines.push('### Validation Errors (observed)');\n for (const ve of result.validationErrors) {\n lines.push(`- **${ve.field}** (${ve.trigger}): \"${ve.errorText}\"`);\n }\n }\n\n if (result.successMessages.length > 0) {\n lines.push('\\n### Success / Feedback Messages (observed)');\n for (const sm of result.successMessages) {\n lines.push(`- ${sm.type}: \"${sm.content}\"`);\n }\n }\n\n if (result.modals.length > 0) {\n lines.push('\\n### Modal / Dialog Contents (observed)');\n for (const m of result.modals) {\n lines.push(`- **Trigger:** ${m.triggerElement}`);\n if (m.title) lines.push(` - Title: ${m.title}`);\n lines.push(` - Content: ${m.content.slice(0, 200)}`);\n if (m.fields.length > 0) lines.push(` - Fields: ${m.fields.join(', ')}`);\n }\n }\n\n if ((result.tabPanels ?? []).length > 0) {\n lines.push('\\n### Tab Panel Contents (observed)');\n for (const tp of result.tabPanels!) {\n lines.push(`- **Tab: ${tp.tabLabel}**${tp.panelHeading ? ` \u2014 ${tp.panelHeading}` : ''}: ${tp.panelContent.slice(0, 200)}`);\n }\n }\n\n if ((result.tooltips ?? []).length > 0) {\n lines.push('\\n### Tooltips (observed)');\n for (const tt of result.tooltips!) {\n lines.push(`- **${tt.triggerLabel}**: ${tt.tooltipText}`);\n }\n }\n\n if ((result.carouselSlides ?? []).length > 0) {\n lines.push('\\n### Carousel Slides (observed)');\n for (const cs of result.carouselSlides!) {\n lines.push(`- **Slide ${cs.slideIndex + 1}**${cs.heading ? ` \u2014 ${cs.heading}` : ''}: ${cs.content.slice(0, 200)}`);\n }\n }\n\n if ((result.wizardSteps ?? []).length > 0) {\n lines.push('\\n### Wizard / Multi-Step Form (observed)');\n for (const ws of result.wizardSteps!) {\n const label = ws.stepLabel ? ` \u2014 ${ws.stepLabel}` : '';\n lines.push(`- **Step ${ws.stepIndex + 1}${label}**: ${ws.content.slice(0, 200)}`);\n if (ws.fields.length > 0) lines.push(` - Fields: ${ws.fields.join(', ')}`);\n }\n }\n\n if ((result.focusTraps ?? []).length > 0) {\n lines.push('\\n### Focus-Trap Audit (observed)');\n for (const ft of result.focusTraps!) {\n const verdict = ft.focusTrapped ? '\u2705 PASS' : '\u274C FAIL (WCAG 2.1.2)';\n lines.push(`- **${ft.modalTrigger}**: ${verdict} \u2014 focused: [${ft.sampledFocused.slice(0, 4).join(', ')}]`);\n }\n }\n\n if (result.conditionals.length > 0) {\n lines.push('\\n### Conditional UI (observed)');\n for (const c of result.conditionals) {\n lines.push(`- **${c.triggerElement}** = \"${c.triggerValue}\" reveals: ${c.revealedText}`);\n }\n }\n\n if (result.loadingPatterns.length > 0) {\n lines.push('\\n### Loading State Text (observed)');\n for (const lp of result.loadingPatterns) lines.push(`- \"${lp}\"`);\n }\n\n const full = lines.join('\\n');\n\n const errorSection = result.validationErrors.length > 0\n ? `## Real Validation Errors \u2014 ${result.url}\\n` +\n result.validationErrors.map((e) => `- **${e.field}**: \"${e.errorText}\"`).join('\\n')\n : '';\n\n const interactionSection = [\n result.successMessages.length > 0\n ? `## Success Messages \u2014 ${result.url}\\n` + result.successMessages.map((s) => `- ${s.type}: \"${s.content}\"`).join('\\n')\n : '',\n result.loadingPatterns.length > 0\n ? `## Loading Patterns \u2014 ${result.url}\\n` + result.loadingPatterns.map((lp) => `- \"${lp}\"`).join('\\n')\n : '',\n result.modals.length > 0\n ? `## Modal Contents \u2014 ${result.url}\\n` + result.modals.map((m) => `### ${m.triggerElement}\\n${m.content}`).join('\\n')\n : '',\n ].filter(Boolean).join('\\n\\n');\n\n return {\n errorCatalogueChunk: errorSection,\n interactionPatternsChunk: interactionSection,\n };\n}\n\n/**\n * Run interactive probing for all screens discovered in a crawl session.\n * Called from crawlProject() when opts.interactive = true.\n */\nexport async function probeAllScreens(\n page: any,\n tenantId: string,\n projectId: string,\n discovered: Array<{ url: string; screenId?: string; elements?: any[] }>,\n onProgress?: (pct: number, step: string) => Promise<void>,\n jobId?: string,\n): Promise<void> {\n const probeable = discovered.filter((d) => d.screenId && d.url && !d.url.includes('#'));\n if (probeable.length === 0) return;\n\n const errorCatalogueChunks: string[] = [];\n const interactionChunks: string[] = [];\n\n for (let i = 0; i < probeable.length; i++) {\n const screen = probeable[i];\n const pct = 88 + Math.round((i / probeable.length) * 10);\n await onProgress?.(pct, `Probing screen ${i + 1}/${probeable.length}: ${screen.url}`);\n\n try {\n // Navigate to screen\n if (page.url() !== screen.url) {\n await page.goto(screen.url, { waitUntil: 'domcontentloaded' });\n await page.waitForTimeout(1500); // SPA hydration\n }\n\n const result = await probeScreen(page, screen.screenId!, screen.url, screen.elements ?? []);\n\n // Phase 5: save structured probe observations (replaces per-re-probe)\n const observations = probeResultToObservations(result);\n if (observations.length > 0) {\n await AppBrain.saveProbeObservations(tenantId, projectId, screen.screenId!, observations).catch(console.warn);\n }\n\n // Store raw probe result as KnowledgeChunk (kept for BM25 / oversized projects)\n const md = probeResultToMarkdown(result);\n if (md.errorCatalogueChunk) errorCatalogueChunks.push(md.errorCatalogueChunk);\n if (md.interactionPatternsChunk) interactionChunks.push(md.interactionPatternsChunk);\n\n const probeMarkdown = [md.errorCatalogueChunk, md.interactionPatternsChunk].filter(Boolean).join('\\n\\n');\n if (probeMarkdown.length > 50) {\n await AppBrain.saveKnowledgeChunk(tenantId, projectId, 'probe', screen.screenId!, probeMarkdown, `Probe \u2014 ${screen.url}`).catch(console.warn);\n }\n\n // Save Virtual Screens \u2014 synthetic URL: parent_url + #probe:<stateId>\n const virtualScreensToSave: Array<{ stateId: string; triggerAction: string; overlayType: string }> = [];\n for (const m of result.modals) {\n if (m.stateId) {\n virtualScreensToSave.push({\n stateId: m.stateId,\n triggerAction: `click ${m.triggerElement}`,\n overlayType: 'modal',\n });\n // Also save as a first-class Screen so it appears in the screen library\n // and participates in test case generation. Use a synthetic URL.\n const modalUrl = `${screen.url}#probe:${m.stateId}`;\n const modalTitle = m.title || `${m.triggerElement} Modal`;\n AppBrain.saveScreen(tenantId, projectId, modalTitle, modalUrl, jobId, 'INTERACTIVE_PROBE')\n .then((modalScreen: any) => {\n // Link modal back to parent via DiscoveredLink\n if (modalScreen?.id && screen.screenId) {\n AppBrain.saveDiscoveredLinks(tenantId, projectId, screen.screenId!, [{\n href: modalUrl,\n text: m.triggerElement,\n linkType: 'BUTTON_LINK',\n source: 'INTERACTIVE',\n toScreenId: modalScreen.id,\n jobId,\n }]).catch(() => {});\n }\n })\n .catch(() => {});\n }\n }\n for (const c of result.conditionals) {\n if ((c as any).stateId) {\n virtualScreensToSave.push({\n stateId: (c as any).stateId,\n triggerAction: `change ${c.triggerElement} to ${c.triggerValue}`,\n overlayType: 'panel',\n });\n }\n }\n if (virtualScreensToSave.length > 0) {\n await (AppBrain as any).saveVirtualScreens(tenantId, projectId, screen.screenId!, virtualScreensToSave).catch(console.warn);\n }\n\n if (result.validationErrors.length > 0) {\n console.log(`[probe] ${screen.url}: ${result.validationErrors.length} validation errors, ${result.modals.length} modals`);\n }\n } catch (err) {\n console.warn(`[probe] \u2717 ${screen.url}:`, err instanceof Error ? err.message : err);\n }\n }\n\n // Update ERROR_CATALOGUE skill file with all real observed errors\n if (errorCatalogueChunks.length > 0) {\n const combined = `# Error Catalogue (Real Observed \u2014 Interactive Probe)\\n\\n${errorCatalogueChunks.join('\\n\\n')}`;\n await AppBrain.upsertSkillFile(tenantId, projectId, 'ERROR_CATALOGUE', combined, {\n summary: `Real validation error messages observed via interactive probe across ${errorCatalogueChunks.length} screens`,\n scope: 'PROJECT',\n }).catch(console.warn);\n }\n\n // Update INTERACTION_PATTERNS skill file with real observed patterns\n if (interactionChunks.length > 0) {\n const combined = `# Interaction Patterns (Real Observed \u2014 Interactive Probe)\\n\\n${interactionChunks.join('\\n\\n')}`;\n await AppBrain.upsertSkillFile(tenantId, projectId, 'INTERACTION_PATTERNS', combined, {\n summary: `Real loading states, success messages, and modal contents observed via interactive probe`,\n scope: 'PROJECT',\n }).catch(console.warn);\n }\n\n await onProgress?.(98, 'Interactive probe complete');\n console.log(`[probe] Probed ${probeable.length} screens. Errors: ${errorCatalogueChunks.length}, Interactions: ${interactionChunks.length}`);\n}\n\n// \u2500\u2500 Keyboard Navigation Probe \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface FocusableElement {\n tag: string;\n role: string | null;\n ariaLabel: string | null;\n text: string | null;\n hasFocusIndicator: boolean;\n tabIndex: number | null;\n}\n\nexport interface KeyboardNavResult {\n focusableCount: number;\n elementsWithNoFocusIndicator: number;\n sequence: FocusableElement[];\n issues: string[];\n}\n\n/** Verify skip-navigation links exist and their targets are present in the DOM. */\nasync function probeSkipNavigation(page: Page): Promise<string[]> {\n const issues: string[] = [];\n try {\n const skipLinks = await page.evaluate(() =>\n Array.from(document.querySelectorAll('a[href^=\"#\"]'))\n .filter((el) => /skip|jump to|main content|content|nav/i.test((el as HTMLElement).innerText?.toLowerCase().trim() || ''))\n .map((el) => ({\n text: (el as HTMLElement).innerText?.trim() || '',\n href: el.getAttribute('href') || '',\n hasTarget: !!document.querySelector(el.getAttribute('href') || '#__invalid__'),\n }))\n );\n if (skipLinks.length === 0) {\n issues.push('No skip navigation links found (WCAG 2.4.1 \u2014 keyboard users cannot bypass nav blocks)');\n }\n for (const link of skipLinks) {\n if (!link.hasTarget) issues.push(`Skip link \"${link.text}\" \u2192 \"${link.href}\" target not found in DOM`);\n }\n } catch { /* non-fatal */ }\n return issues;\n}\n\n/** Right-click on canvas, chart, and context-menu elements to capture context menu items. */\nasync function probeContextMenus(page: Page): Promise<ContextMenuCapture[]> {\n const results: ContextMenuCapture[] = [];\n try {\n const targets = await page.$$('canvas, [data-contextmenu], [oncontextmenu], [class*=\"chart\" i]:not(script)');\n for (const t of targets.slice(0, 4)) {\n try {\n await t.click({ button: 'right', timeout: 1000 });\n await page.waitForTimeout(350);\n const items = await page.evaluate(() =>\n Array.from(document.querySelectorAll('[role=\"menu\"] [role=\"menuitem\"], .context-menu-item, [class*=\"contextmenu\" i] li'))\n .map((el) => (el as HTMLElement).innerText?.trim())\n .filter(Boolean).slice(0, 12)\n );\n if (items.length > 0) {\n const trigger = await t.evaluate((el) => (el as HTMLElement).getAttribute('aria-label') || (el as HTMLElement).className.slice(0, 40) || el.tagName).catch(() => '');\n results.push({ trigger, items });\n }\n await page.keyboard.press('Escape').catch(() => { });\n } catch { /* skip this target */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Click carousel next buttons up to 8 slides and record content of each revealed slide. */\nasync function probeCarousels(page: Page): Promise<CarouselSlide[]> {\n const results: CarouselSlide[] = [];\n try {\n const CAROUSEL_SEL = '[class*=\"carousel\" i], [class*=\"slider\" i], [class*=\"swiper\" i], [role=\"region\"][aria-roledescription*=\"carousel\" i]';\n const hasCarousel = await page.evaluate((sel) => !!document.querySelector(sel), CAROUSEL_SEL);\n if (!hasCarousel) return results;\n\n const NEXT_SEL = '[aria-label*=\"next\" i], [aria-label*=\"Next\"], [class*=\"next\" i]:not([class*=\"footnext\"]):not(a), button[class*=\"arrow-right\" i], button[class*=\"chevron-right\" i]';\n\n // Capture slide 0 (current state)\n const slide0 = await page.evaluate((carSel) => {\n const car = document.querySelector(carSel) as HTMLElement | null;\n if (!car) return null;\n const active = car.querySelector('[aria-hidden=\"false\"], .active, [class*=\"active\" i], [class*=\"current\" i]') as HTMLElement | null;\n const target = active ?? car;\n return { heading: target.querySelector('h2,h3,h4')?.textContent?.trim() ?? '', content: target.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 300) ?? '' };\n }, CAROUSEL_SEL);\n if (slide0?.content && slide0.content.length > 15) results.push({ slideIndex: 0, heading: slide0.heading || undefined, content: slide0.content });\n\n const seen = new Set<string>(slide0?.content ? [slide0.content] : []);\n for (let i = 1; i < 8; i++) {\n try {\n const nextBtn = page.locator(NEXT_SEL).first();\n const visible = await nextBtn.isVisible().catch(() => false);\n if (!visible) break;\n await nextBtn.click({ timeout: 1000 });\n await page.waitForTimeout(600);\n const slide = await page.evaluate((carSel) => {\n const car = document.querySelector(carSel) as HTMLElement | null;\n if (!car) return null;\n const active = car.querySelector('[aria-hidden=\"false\"], .active, [class*=\"active\" i], [class*=\"current\" i]') as HTMLElement | null;\n const target = active ?? car;\n return { heading: target.querySelector('h2,h3,h4')?.textContent?.trim() ?? '', content: target.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 300) ?? '' };\n }, CAROUSEL_SEL);\n if (!slide?.content || slide.content.length < 15 || seen.has(slide.content)) break;\n seen.add(slide.content);\n results.push({ slideIndex: i, heading: slide.heading || undefined, content: slide.content });\n } catch { break; }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Follow Next/Continue buttons to capture each wizard step's fields and content. */\nasync function probeWizardSteps(page: Page): Promise<WizardStep[]> {\n const results: WizardStep[] = [];\n try {\n const NEXT_LABELS = /^(next|continue|proceed|step \\d|\u203A|\u2192|forward)/i;\n const BACK_LABELS = /^(back|previous|prev|\u2039|\u2190)/i;\n const WIZARD_INDICATORS = '[role=\"progressbar\"], [class*=\"stepper\" i], [class*=\"wizard\" i], [class*=\"progress-step\" i], [aria-label*=\"step\" i]';\n const isWizard = await page.evaluate((sel) => !!document.querySelector(sel), WIZARD_INDICATORS);\n if (!isWizard) return results;\n\n const captureStep = async (stepIndex: number): Promise<WizardStep> => {\n const data = await page.evaluate(() => {\n const content = (document.querySelector('main, [role=\"main\"], form') as HTMLElement | null)?.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) ?? '';\n const stepLabel = document.querySelector('[aria-current=\"step\"], [class*=\"step-label\" i], [class*=\"stepper-label\" i]')?.textContent?.trim() ?? '';\n const fields = Array.from(document.querySelectorAll('input:not([type=\"hidden\"]), select, textarea'))\n .map((el) => (el as HTMLElement).getAttribute('aria-label') || (el as HTMLInputElement).placeholder || (el as HTMLInputElement).name || '')\n .filter(Boolean).slice(0, 10);\n return { content, stepLabel, fields };\n });\n return { stepIndex, stepLabel: data.stepLabel || undefined, content: data.content, fields: data.fields };\n };\n\n results.push(await captureStep(0));\n\n for (let i = 1; i < 6; i++) {\n try {\n const nextLabel = await page.evaluate((nextRe) => {\n const btns = Array.from(document.querySelectorAll('button, [role=\"button\"]'));\n const btn = btns.find((b) => new RegExp(nextRe).test((b as HTMLElement).innerText?.trim() || ''));\n return (btn as HTMLElement)?.innerText?.trim() || null;\n }, NEXT_LABELS.source);\n if (!nextLabel) break;\n const btn = page.locator(`button:has-text(\"${nextLabel}\")`).first();\n await btn.click({ timeout: 1500 });\n await page.waitForTimeout(800);\n results.push(await captureStep(i));\n } catch { break; }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** For each open modal, Tab 8\u00D7 and verify focus stays inside the dialog (focus-trap check). */\nasync function detectFocusTraps(page: Page): Promise<FocusTrapResult[]> {\n const results: FocusTrapResult[] = [];\n try {\n const openDialogs = await page.evaluate(() => {\n return Array.from(document.querySelectorAll('[role=\"dialog\"], [role=\"alertdialog\"]'))\n .filter((el) => window.getComputedStyle(el).display !== 'none')\n .map((el) => el.getAttribute('aria-label') || el.querySelector('[role=\"heading\"]')?.textContent?.trim() || 'dialog');\n });\n for (const dialogLabel of openDialogs.slice(0, 3)) {\n const sampled: string[] = [];\n let allInside = true;\n for (let i = 0; i < 8; i++) {\n await page.keyboard.press('Tab');\n await page.waitForTimeout(80);\n const focused = await page.evaluate(() => {\n const el = document.activeElement as HTMLElement | null;\n const inDialog = !!el?.closest('[role=\"dialog\"], [role=\"alertdialog\"]');\n return { label: el?.getAttribute('aria-label') || el?.textContent?.trim().slice(0, 40) || el?.tagName || '', inDialog };\n });\n sampled.push(focused.label);\n if (!focused.inDialog) { allInside = false; break; }\n }\n results.push({ modalTrigger: dialogLabel, focusTrapped: allInside, sampledFocused: sampled });\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Click each visible tab and record the panel content revealed. */\nasync function probeTabPanels(page: Page): Promise<TabPanelCapture[]> {\n const results: TabPanelCapture[] = [];\n try {\n const tabs = await page.$$('[role=\"tab\"]:not([aria-disabled=\"true\"])');\n for (const tab of tabs.slice(0, 10)) {\n try {\n const tabLabel = await tab.evaluate((el) => (el as HTMLElement).innerText?.trim() || el.getAttribute('aria-label') || '');\n if (!tabLabel) continue;\n await tab.click({ timeout: 1000 });\n await page.waitForTimeout(400);\n const panelContent = await page.evaluate(() => {\n const panel = document.querySelector('[role=\"tabpanel\"]:not([hidden])') as HTMLElement | null;\n if (!panel) return null;\n const style = window.getComputedStyle(panel);\n if (style.display === 'none' || style.visibility === 'hidden') return null;\n return {\n heading: panel.querySelector('h2,h3,h4')?.textContent?.trim() ?? '',\n content: panel.innerText?.replace(/\\s+/g, ' ').trim().slice(0, 400) ?? '',\n };\n });\n if (panelContent?.content && panelContent.content.length > 15) {\n results.push({ tabLabel: tabLabel.slice(0, 60), panelContent: panelContent.content, panelHeading: panelContent.heading || undefined });\n }\n } catch { /* this tab failed, try next */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/** Hover elements with title/aria-describedby to capture tooltip text. */\nasync function probeTooltips(page: Page): Promise<TooltipCapture[]> {\n const results: TooltipCapture[] = [];\n try {\n const candidates = await page.evaluate(() => {\n return Array.from(document.querySelectorAll('[title], [aria-describedby], [data-tooltip], [data-tip], [class*=\"tooltip\" i]'))\n .filter((el) => {\n const style = window.getComputedStyle(el as HTMLElement);\n return style.display !== 'none' && style.visibility !== 'hidden';\n })\n .slice(0, 8)\n .map((el) => ({\n label: (el as HTMLElement).innerText?.trim().slice(0, 60) || el.getAttribute('aria-label') || '',\n title: el.getAttribute('title') || '',\n describedBy: el.getAttribute('aria-describedby') || '',\n selector: el.tagName.toLowerCase() + (el.id ? `#${el.id}` : ''),\n }));\n });\n for (const c of candidates) {\n try {\n // Prefer title attribute directly \u2014 no need to hover for static tooltips\n if (c.title && c.title.length > 5) {\n results.push({ triggerLabel: c.label || c.selector, tooltipText: c.title.slice(0, 200) });\n continue;\n }\n if (c.describedBy) {\n const tooltipText = await page.evaluate((id) => document.getElementById(id)?.textContent?.trim() ?? '', c.describedBy);\n if (tooltipText && tooltipText.length > 5) {\n results.push({ triggerLabel: c.label || c.selector, tooltipText: tooltipText.slice(0, 200) });\n }\n }\n } catch { /* skip this element */ }\n }\n } catch { /* non-fatal */ }\n return results;\n}\n\n/**\n * Probe keyboard navigation accessibility by pressing Tab up to 50 times and\n * recording each focused element's properties and focus-indicator visibility.\n * Stops early if focus wraps back to the start or hits the same element twice.\n */\nexport async function probeKeyboardNav(page: Page): Promise<KeyboardNavResult> {\n const sequence: FocusableElement[] = [];\n const issues: string[] = [];\n const seenKeys = new Set<string>();\n let firstKey: string | null = null;\n\n for (let i = 0; i < 50; i++) {\n await page.keyboard.press('Tab');\n\n const el = await page.evaluate(() => {\n const active = document.activeElement as HTMLElement | null;\n if (!active || active === document.body) return null;\n const outline = window.getComputedStyle(active).outline;\n return {\n tag: active.tagName.toLowerCase(),\n role: active.getAttribute('role'),\n ariaLabel: active.getAttribute('aria-label'),\n text: (active.innerText ?? '').trim().slice(0, 60) || null,\n outline,\n tabIndex: active.tabIndex ?? null,\n };\n });\n\n if (!el) break;\n\n const hasFocusIndicator =\n el.outline !== 'none' &&\n el.outline !== '' &&\n !el.outline.startsWith('0px') &&\n !el.outline.startsWith('rgba(0, 0, 0, 0)') &&\n !el.outline.startsWith('rgba(0,0,0,0)');\n\n const focusable: FocusableElement = {\n tag: el.tag,\n role: el.role,\n ariaLabel: el.ariaLabel,\n text: el.text,\n hasFocusIndicator,\n tabIndex: el.tabIndex,\n };\n\n const key = `${el.tag}|${el.text ?? ''}|${el.role ?? ''}`;\n\n if (i === 0) {\n firstKey = key;\n } else if (key === firstKey || seenKeys.has(key)) {\n // Focus wrapped back to start or hit a duplicate \u2014 stop probing\n break;\n }\n\n seenKeys.add(key);\n sequence.push(focusable);\n\n if (!hasFocusIndicator) {\n issues.push(`Element #${i + 1} (${el.tag}) has no visible focus indicator`);\n }\n }\n\n return {\n focusableCount: sequence.length,\n elementsWithNoFocusIndicator: issues.length,\n sequence,\n issues,\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["btn"]
7
7
  }