// Responsibility: Generate the repo-owned Cloud Console route-matrix browser command.
import type { HwlabRuntimeWebProbeConsoleVerificationProfileSpec } from "./hwlab-node-web-probe-console-profile";

export function nodeWebProbeConsoleVerifyScript(
  profileName: string,
  profile: HwlabRuntimeWebProbeConsoleVerificationProfileSpec,
  configRef: string,
): string {
  const config = { profileName, configRef, ...profile };
  return String.raw`const config = ${JSON.stringify(config)};

export default async function consoleVerify({ page, context, goto, reloadStable, wait, screenshot, jsonArtifact, recordStep }) {
  const failures = [];
  const pages = [];
  const screenshots = [];
  const pageErrors = [];
  const consoleErrors = [];
  const requestFailures = [];
  const responseErrors = [];
  let totalFailureCount = 0;
  let totalPageErrorCount = 0;
  let totalConsoleErrorCount = 0;
  let totalRequestFailureCount = 0;
  let totalResponseErrorCount = 0;

  const addFailure = (message) => {
    totalFailureCount += 1;
    if (failures.length < config.outputLimits.failures) failures.push(String(message));
  };
  const addConsoleError = (message) => {
    totalConsoleErrorCount += 1;
    if (consoleErrors.length < config.outputLimits.consoleErrors) consoleErrors.push(String(message).slice(0, 300));
  };
  const addPageError = (error) => {
    totalPageErrorCount += 1;
    if (pageErrors.length < config.outputLimits.pageErrors) {
      pageErrors.push(String(error?.stack || errorMessage(error)).slice(0, 600));
    }
  };
  const addRequestFailure = (request) => {
    totalRequestFailureCount += 1;
    if (requestFailures.length < config.outputLimits.requestFailures) {
      requestFailures.push({
        method: request.method(),
        url: request.url().replace(/[?#].*$/u, ""),
        errorText: String(request.failure()?.errorText || "request failed").slice(0, 300),
      });
    }
  };
  const addResponseError = (response) => {
    totalResponseErrorCount += 1;
    if (responseErrors.length < config.outputLimits.responseErrors) {
      responseErrors.push({ status: response.status(), url: response.url().replace(/[?#].*$/u, "") });
    }
  };

  await context.addInitScript(() => {
    const OriginalEventSource = window.EventSource;
    window.__unideskConsoleEventSources = [];
    if (!OriginalEventSource || window.__unideskConsoleEventSourceWrapped === true) return;
    window.__unideskConsoleEventSourceWrapped = true;
    function TrackedEventSource(url, options) {
      const instance = new OriginalEventSource(url, options);
      window.__unideskConsoleEventSources.push(String(url));
      return instance;
    }
    TrackedEventSource.prototype = OriginalEventSource.prototype;
    TrackedEventSource.CONNECTING = OriginalEventSource.CONNECTING;
    TrackedEventSource.OPEN = OriginalEventSource.OPEN;
    TrackedEventSource.CLOSED = OriginalEventSource.CLOSED;
    window.EventSource = TrackedEventSource;
  });

  page.on("console", (message) => {
    if (message.type() === "error") addConsoleError(message.text());
  });
  page.on("pageerror", addPageError);
  page.on("requestfailed", addRequestFailure);
  page.on("response", (response) => {
    if (response.status() >= 500) addResponseError(response);
  });

  const routeById = new Map(config.routes.map((route) => [route.id, route]));
  const requiredRoute = (id) => {
    const route = routeById.get(id);
    if (!route) throw new Error("console verification profile references missing route " + id);
    return route;
  };
  const gotoRoute = async (id) => {
    const route = requiredRoute(id);
    await goto(route.path, { selectors: [route.readySelector], readinessTimeoutMs: config.navigationTimeoutMs, attempts: 3 });
    if (config.settleMs > 0) await wait(config.settleMs);
    return route;
  };
  const captureScreenshot = async (name) => {
    if (screenshots.length >= config.outputLimits.screenshots) {
      addFailure("screenshot budget exceeded before " + name);
      return null;
    }
    try {
      const artifact = await screenshot(name);
      screenshots.push(artifact);
      return artifact;
    } catch (error) {
      addFailure("screenshot " + name + " failed: " + errorMessage(error));
      return null;
    }
  };

  for (const viewport of config.viewports) {
    await page.setViewportSize({ width: viewport.width, height: viewport.height });
    const beforeCount = pages.length;
    for (const route of config.routes) {
      await gotoRoute(route.id);
      const metrics = await page.evaluate(({ requestedRoute, viewportId, readySelector, textPreviewChars }) => {
        const html = document.documentElement;
        const body = document.body;
        const app = document.querySelector("#app");
        const root = document.querySelector(readySelector);
        const text = (app?.textContent || "").replace(/\s+/gu, " ").trim();
        const rootRect = root?.getBoundingClientRect();
        return {
          requestedRoute,
          viewportId,
          finalPath: location.pathname + location.search,
          title: document.title,
          textLength: text.length,
          textPreview: text.slice(0, textPreviewChars),
          appVisible: Boolean(app && app.getBoundingClientRect().width > 0 && app.getBoundingClientRect().height > 0),
          rootVisible: Boolean(rootRect && rootRect.width > 0 && rootRect.height > 0),
          horizontalOverflowPx: Math.max(0, html.scrollWidth - innerWidth, body.scrollWidth - innerWidth),
          documentHeight: Math.max(html.scrollHeight, body.scrollHeight),
        };
      }, {
        requestedRoute: route.path,
        viewportId: viewport.id,
        readySelector: route.readySelector,
        textPreviewChars: config.outputLimits.textPreviewChars,
      });
      pages.push(metrics);
      if (!metrics.appVisible || !metrics.rootVisible || metrics.textLength < config.minTextLength) {
        addFailure(viewport.id + " " + route.path + ": app/root blank");
      }
      if (metrics.finalPath.startsWith("/login")) addFailure(viewport.id + " " + route.path + ": redirected to login");
      if (metrics.horizontalOverflowPx > config.maxHorizontalOverflowPx) {
        addFailure(viewport.id + " " + route.path + ": horizontal overflow " + metrics.horizontalOverflowPx + "px");
      }
      if (route.screenshot) await captureScreenshot(viewport.id + "-" + route.id + ".png");
    }
    recordStep("console-matrix-" + viewport.id, {
      ok: totalFailureCount === 0,
      viewport,
      routeSamples: pages.length - beforeCount,
      totalFailureCount,
    });
  }

  const workflowEvidence = {};
  if (config.workflows.projects) workflowEvidence.projects = await verifyProjects(config.workflows.projects);
  if (config.workflows.hwpod) workflowEvidence.hwpod = await verifyHwpod(config.workflows.hwpod);
  if (config.workflows.mdtodo) workflowEvidence.mdtodo = await verifyMdtodo(config.workflows.mdtodo);
  if (config.workflows.tasktree) workflowEvidence.tasktree = await verifyTasktree(config.workflows.tasktree);
  if (config.workflows.agentObserver) workflowEvidence.agentObserver = await verifyAgentObserver(config.workflows.agentObserver);

  if (totalResponseErrorCount > 0) addFailure("HTTP 5xx responses: " + totalResponseErrorCount);
  const evidence = {
    profileName: config.profileName,
    configRef: config.configRef,
    pages,
    failures,
    failureCount: totalFailureCount,
    consoleErrors,
    consoleErrorCount: totalConsoleErrorCount,
    pageErrors,
    pageErrorCount: totalPageErrorCount,
    requestFailures,
    requestFailureCount: totalRequestFailureCount,
    responseErrors,
    responseErrorCount: totalResponseErrorCount,
    screenshots,
    workflowEvidence,
    valuesRedacted: true,
  };
  const report = await jsonArtifact("cloud-console-verification.json", evidence);
  const ok = totalFailureCount === 0;
  recordStep("console-verification-final", {
    ok,
    profileName: config.profileName,
    routeSamples: pages.length,
    failureCount: totalFailureCount,
    screenshotCount: screenshots.length,
  });
  return {
    ok,
    status: ok ? "pass" : "blocked",
    pageErrors,
    consoleErrors,
    requestFailures,
    responseErrors,
    failureCount: totalFailureCount,
    failures,
    pageErrorCount: totalPageErrorCount,
    consoleErrorCount: totalConsoleErrorCount,
    requestFailureCount: totalRequestFailureCount,
    responseErrorCount: totalResponseErrorCount,
    profileName: config.profileName,
    configRef: config.configRef,
    routeSamples: pages.length,
    viewportCount: config.viewports.length,
    screenshotCount: screenshots.length,
    report,
    workflowEvidence,
    issueEvidence: {
      profileName: config.profileName,
      configRef: config.configRef,
      reportSha256: typeof report?.sha256 === "string" ? report.sha256 : null,
      screenshotSha256: screenshots.map((item) => item?.sha256).filter((item) => typeof item === "string"),
      failureCount: totalFailureCount,
      failures,
      pageErrors,
      consoleErrors,
      requestFailures,
      responseErrors,
      valuesRedacted: true,
    },
    valuesRedacted: true,
  };

  async function verifyProjects(workflow) {
    await gotoRoute(workflow.routeId);
    const button = page.getByRole("button", { name: workflow.listButtonName, exact: true });
    if (await button.count() < 1) {
      addFailure("Projects: list view control missing");
      return { listView: false };
    }
    await button.first().click();
    await wait(config.settleMs);
    const queryPresent = new URL(page.url()).searchParams.has(workflow.viewQueryKey);
    if (!queryPresent) addFailure("Projects: list view did not enter URL state");
    return { listView: queryPresent, finalPath: new URL(page.url()).pathname + new URL(page.url()).search };
  }

  async function verifyHwpod(workflow) {
    await gotoRoute(workflow.devicesRouteId);
    const listButton = page.getByRole("button", { name: workflow.listButtonName, exact: true });
    if (await listButton.count() < 1) {
      addFailure("HWPOD devices: list view control missing");
    } else {
      await listButton.first().click();
      await wait(config.settleMs);
      if (await page.locator(workflow.devicesListSelector).count() < 1) addFailure("HWPOD devices: list projection missing");
      const cardButton = page.getByRole("button", { name: workflow.cardButtonName, exact: true });
      if (await cardButton.count() < 1) addFailure("HWPOD devices: card view control missing");
      else {
        await cardButton.first().click();
        await wait(config.settleMs);
        if (await page.locator(workflow.devicesCardSelector).count() < 1) addFailure("HWPOD devices: card projection missing");
      }
    }
    await page.emulateMedia({ reducedMotion: "reduce" });
    const reducedMotion = await page.evaluate((selector) => ({
      matches: matchMedia("(prefers-reduced-motion: reduce)").matches,
      animations: [...document.querySelectorAll(selector)].map((element) => getComputedStyle(element).animationName),
    }), workflow.pcbTraceSelector);
    if (!reducedMotion.matches || reducedMotion.animations.some((name) => name !== "none")) {
      addFailure("HWPOD devices: reduced-motion does not stop PCB animation");
    }
    await page.emulateMedia({ reducedMotion: "no-preference" });
    let deviceDeepLink = null;
    const detail = page.getByRole("button", { name: workflow.detailButtonName, exact: true }).first();
    if (await detail.count() > 0) {
      await detail.click();
      await wait(config.settleMs);
      deviceDeepLink = new URL(page.url()).pathname;
      const basePath = requiredRoute(workflow.devicesRouteId).path.split("?")[0];
      if (!deviceDeepLink.startsWith(workflow.detailPathPrefix) || deviceDeepLink === basePath) {
        addFailure("HWPOD devices: invalid detail deep link " + deviceDeepLink);
      }
      await reloadStable({ selectors: [requiredRoute(workflow.devicesRouteId).readySelector], readinessTimeoutMs: config.navigationTimeoutMs });
      if (new URL(page.url()).pathname !== deviceDeepLink) addFailure("HWPOD devices: deep link did not survive refresh");
    }

    await gotoRoute(workflow.nodesRouteId);
    const nodeListButton = page.getByRole("button", { name: workflow.listButtonName, exact: true });
    if (await nodeListButton.count() < 1) addFailure("HWPOD nodes: list view control missing");
    else {
      await nodeListButton.first().click();
      await wait(config.settleMs);
      if (await page.locator(workflow.nodesListSelector).count() < 1) addFailure("HWPOD nodes: list projection missing");
    }

    await gotoRoute(workflow.onboardingRouteId);
    const onboarding = await page.evaluate((workflowConfig) => ({
      commands: [...document.querySelectorAll(workflowConfig.onboardingCommandSelector)].map((element) => element.textContent?.trim() || ""),
      downloadHref: document.querySelector(workflowConfig.downloadSelector)?.getAttribute("href") || null,
      internalVisibleCount: document.querySelectorAll(workflowConfig.forbiddenInternalSelector).length,
    }), workflow);
    for (const prefix of workflow.requiredCommandPrefixes) {
      if (!onboarding.commands.some((command) => command.startsWith(prefix))) addFailure("HWPOD onboarding: required command prefix missing: " + prefix);
    }
    for (const forbidden of workflow.forbiddenDownloadSubstrings) {
      if (onboarding.downloadHref?.includes(forbidden)) addFailure("HWPOD onboarding: forbidden download endpoint visible: " + forbidden);
    }
    if (onboarding.internalVisibleCount > 0) addFailure("HWPOD onboarding: internal entrypoint token visible");
    return { deviceDeepLink, reducedMotion, onboarding };
  }

  async function verifyMdtodo(workflow) {
    await gotoRoute(workflow.routeId);
    const firstTask = page.locator(workflow.taskSelector).first();
    await firstTask.waitFor({ state: "visible", timeout: config.navigationTimeoutMs }).catch(() => undefined);
    if (await firstTask.count() < 1) {
      addFailure("MDTODO: projected task tree is empty");
      return { taskDeepLink: null, reportPreviewVisible: false, reportFullscreenVisible: false };
    }
    await firstTask.click();
    await wait(config.settleMs);
    const taskDeepLink = new URL(page.url()).pathname;
    if (!taskDeepLink.includes(workflow.taskDeepLinkSegment)) addFailure("MDTODO: task deep link missing " + taskDeepLink);
    await reloadStable({ selectors: [workflow.taskDetailSelector], readinessTimeoutMs: config.navigationTimeoutMs });
    if (new URL(page.url()).pathname !== taskDeepLink) addFailure("MDTODO: task deep link did not survive refresh");
    let reportPreviewVisible = false;
    let reportFullscreenVisible = false;
    const reportLink = page.locator(workflow.reportLinkSelector).first();
    if (await reportLink.count() > 0) {
      await reportLink.click();
      await wait(config.settleMs);
      reportPreviewVisible = await page.locator(workflow.reportPreviewSelector).isVisible().catch(() => false);
      if (!reportPreviewVisible) addFailure("MDTODO: report preview did not become visible");
      const fullscreen = page.locator(workflow.fullscreenButtonSelector).first();
      if (await fullscreen.count() > 0) {
        await fullscreen.click();
        await wait(config.settleMs);
        reportFullscreenVisible = await page.locator(workflow.fullscreenDialogSelector).isVisible().catch(() => false);
        if (!reportFullscreenVisible) addFailure("MDTODO: report fullscreen did not become visible");
        await page.keyboard.press("Escape");
      }
    }
    return { taskDeepLink, reportPreviewVisible, reportFullscreenVisible };
  }

  async function verifyTasktree(workflow) {
    await gotoRoute(workflow.routeId);
    const firstTask = page.locator(workflow.taskSelector).first();
    await firstTask.waitFor({ state: "visible", timeout: config.navigationTimeoutMs }).catch(() => undefined);
    if (await firstTask.count() < 1) {
      addFailure("TaskTree: timeline is empty");
      return { taskDeepLink: null, taskCount: 0, visibleBarCount: 0, reportCount: 0, dialogVisible: false };
    }
    const timeline = await page.evaluate((workflowConfig) => {
      const visible = (element) => {
        const rect = element.getBoundingClientRect();
        const style = getComputedStyle(element);
        return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden";
      };
      const gantt = document.querySelector(workflowConfig.ganttSelector);
      const scroll = document.querySelector(workflowConfig.scrollSelector);
      const labels = [...document.querySelectorAll(workflowConfig.taskSelector)];
      const bars = [...document.querySelectorAll(workflowConfig.barSelector)];
      return {
        taskCount: labels.length,
        datedTaskCount: labels.filter((element) => element.getAttribute("data-dated") === "true").length,
        visibleLabelCount: labels.filter(visible).length,
        barCount: bars.length,
        visibleBarCount: bars.filter(visible).length,
        milestoneCount: document.querySelectorAll(workflowConfig.milestoneSelector).length,
        ganttInternalOverflowPx: scroll ? Math.max(0, scroll.scrollWidth - scroll.clientWidth) : null,
        splitPane: Boolean(gantt && scroll && !scroll.contains(labels[0])),
        labelsWithVisibleText: labels.filter((element) => (element.textContent || "").trim().length > 0).length,
      };
    }, workflow);
    if (timeline.taskCount < 1 || timeline.visibleLabelCount !== timeline.taskCount) addFailure("TaskTree: task labels are not visible");
    if (timeline.visibleBarCount !== timeline.datedTaskCount || timeline.labelsWithVisibleText !== timeline.taskCount) addFailure("TaskTree: dated Gantt bars or labels are inconsistent");
    const zoomIn = page.getByRole("button", { name: workflow.zoomInButtonName, exact: true });
    if (await zoomIn.count() < 1) {
      addFailure("TaskTree: timeline zoom control missing");
    } else {
      for (let attempt = 0; attempt < 4; attempt += 1) {
        const scale = (await page.locator(workflow.scaleSelector).textContent().catch(() => ""))?.trim();
        if (scale === workflow.secondsScaleLabel) break;
        await zoomIn.click();
        await wait(config.settleMs);
      }
      const zoomedTimeline = await page.evaluate((workflowConfig) => {
        const scroll = document.querySelector(workflowConfig.scrollSelector);
        if (!(scroll instanceof HTMLElement)) return null;
        scroll.scrollLeft = Math.max(0, (scroll.scrollWidth - scroll.clientWidth) * 0.4);
        scroll.dispatchEvent(new Event("scroll"));
        return { clientWidth: scroll.clientWidth, scrollWidth: scroll.scrollWidth };
      }, workflow);
      await wait(config.settleMs);
      const pinnedColumn = await page.evaluate((workflowConfig) => {
        const scroll = document.querySelector(workflowConfig.scrollSelector);
        const labelPane = document.querySelector(workflowConfig.labelPaneSelector);
        const label = document.querySelector(workflowConfig.taskSelector);
        const scale = document.querySelector(workflowConfig.scaleSelector)?.textContent?.trim() || "";
        if (!(scroll instanceof HTMLElement) || !(labelPane instanceof HTMLElement) || !(label instanceof HTMLElement)) return null;
        const labelPaneRect = labelPane.getBoundingClientRect();
        const labelRect = label.getBoundingClientRect();
        const sampleX = Math.min(labelRect.right - 4, labelRect.left + Math.max(12, labelRect.width / 2));
        const sampleY = labelRect.top + labelRect.height / 2;
        const topElement = document.elementFromPoint(sampleX, sampleY);
        const tickLabels = [...document.querySelectorAll(workflowConfig.tickSelector)];
        return {
          scale,
          labelLeftDelta: Math.round(labelRect.left - labelPaneRect.left),
          labelOwnsHitPoint: Boolean(topElement && (topElement === label || label.contains(topElement))),
          labelInsideScrollPane: scroll.contains(label),
          labelTransform: getComputedStyle(label).transform,
          tickLabelsFit: tickLabels.length > 0 && tickLabels.every((tick) => tick.scrollWidth <= tick.clientWidth + 1 && tick.scrollHeight <= tick.clientHeight + 1),
          scrollLeft: Math.round(scroll.scrollLeft),
        };
      }, workflow);
      if (!zoomedTimeline || zoomedTimeline.scrollWidth <= zoomedTimeline.clientWidth) addFailure("TaskTree: zoomed timeline is not horizontally scrollable");
      if (!pinnedColumn || pinnedColumn.scale !== workflow.secondsScaleLabel) addFailure("TaskTree: timeline did not reach seconds scale");
      if (!timeline.splitPane || !pinnedColumn || Math.abs(pinnedColumn.labelLeftDelta) > 2 || !pinnedColumn.labelOwnsHitPoint || pinnedColumn.labelInsideScrollPane || pinnedColumn.labelTransform !== "none") {
        addFailure("TaskTree: task label is obscured after horizontal scrolling");
      }
      if (!pinnedColumn?.tickLabelsFit) addFailure("TaskTree: seconds-scale tick labels are clipped");
      await captureScreenshot(workflow.zoomScreenshotName);
      timeline.zoomed = { ...zoomedTimeline, ...pinnedColumn };
    }
    await firstTask.click();
    await wait(config.settleMs);
    const taskDeepLink = new URL(page.url()).pathname;
    if (!taskDeepLink.startsWith(requiredRoute(workflow.routeId).path + "/")) addFailure("TaskTree: group deep link missing " + taskDeepLink);
    const groupTask = page.locator(workflow.taskSelector).first();
    await groupTask.waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
    await groupTask.click();
    await page.locator(workflow.detailSelector).waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
    await wait(config.settleMs);
    const detail = await page.evaluate((workflowConfig) => {
      const detailPanel = document.querySelector(workflowConfig.detailSelector);
      const dialog = document.querySelector(workflowConfig.dialogSelector);
      const docked = document.querySelector(workflowConfig.dockedSelector);
      const rect = detailPanel?.getBoundingClientRect();
      return {
        detailVisible: Boolean(rect && rect.width > 0 && rect.height > 0),
        detailInsideViewport: Boolean(rect && rect.left >= 0 && rect.top >= 0 && rect.right <= innerWidth + 1 && rect.bottom <= innerHeight + 1),
        detailMode: dialog ? "dialog" : docked ? "docked" : "unknown",
        reportCount: detailPanel?.querySelectorAll(workflowConfig.reportSelector).length || 0,
      };
    }, workflow);
    if (!detail.detailVisible || !detail.detailInsideViewport) addFailure("TaskTree: detail panel is not bounded by the viewport");
    if (detail.detailMode === "unknown") addFailure("TaskTree: detail panel is neither dialog nor docked");
    if (detail.reportCount < 1) addFailure("TaskTree: selected task has no execution report");
    const artifact = await captureScreenshot(workflow.detailScreenshotName);
    if (detail.detailMode === "dialog") await page.keyboard.press("Escape");
    await reloadStable({ selectors: [requiredRoute(workflow.routeId).readySelector], readinessTimeoutMs: config.navigationTimeoutMs });
    if (new URL(page.url()).pathname !== taskDeepLink) addFailure("TaskTree: group deep link did not survive refresh");
    return { taskDeepLink, ...timeline, ...detail, detailScreenshotSha256: artifact?.sha256 || null };
  }

  async function verifyAgentObserver(workflow) {
    await gotoRoute(workflow.routeId);
    if (workflow.streamSettleMs > 0) await wait(workflow.streamSettleMs);
    const before = await agentObserverState(workflow);
    if (before.sourceCount !== 1) addFailure("AgentRun: expected one product EventSource, observed " + before.sourceCount);
    const viewResults = [];
    for (const view of workflow.views) {
      const button = page.getByRole("button", { name: view.buttonName, exact: true });
      if (await button.count() < 1) {
        addFailure("AgentRun: missing view button " + view.buttonName);
        continue;
      }
      await button.first().click();
      await wait(config.settleMs);
      const queryValue = new URL(page.url()).searchParams.get("view");
      if (queryValue !== view.queryValue) addFailure("AgentRun: view " + view.buttonName + " URL state expected " + view.queryValue + ", got " + queryValue);
      const artifact = await captureScreenshot(view.screenshotName);
      viewResults.push({ buttonName: view.buttonName, queryValue, screenshotSha256: artifact?.sha256 || null });
    }
    let selection = null;
    if (workflow.selection) {
      const candidate = page.locator(workflow.selection.selector).first();
      if (await candidate.count() < 1) {
        addFailure("AgentRun: selection target missing " + workflow.selection.selector);
      } else {
        await candidate.focus();
        await page.keyboard.press("Enter");
        await page.locator(workflow.selection.inspectorSelector).waitFor({ state: "visible", timeout: config.navigationTimeoutMs });
        await wait(config.settleMs);
        const layout = await page.evaluate(({ inspectorSelector }) => {
          const bounds = (selector) => {
            const rect = document.querySelector(selector)?.getBoundingClientRect();
            return rect ? { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom, width: rect.width, height: rect.height } : null;
          };
          const edges = Array.from(document.querySelectorAll(".agent-mind-map-flow .vue-flow__edge-path")).map((edge) => {
            const style = getComputedStyle(edge);
            const length = typeof edge.getTotalLength === "function" ? edge.getTotalLength() : 0;
            const strokeWidth = Number.parseFloat(style.strokeWidth);
            const opacity = Number.parseFloat(style.opacity);
            const visible = length > 8
              && style.display !== "none"
              && style.visibility !== "hidden"
              && opacity > 0
              && strokeWidth >= 1
              && style.stroke !== "none"
              && style.stroke !== "rgba(0, 0, 0, 0)";
            return { length, stroke: style.stroke, strokeWidth, opacity, display: style.display, visibility: style.visibility, visible };
          });
          const inspector = bounds(inspectorSelector);
          const content = bounds(".agent-observer-content");
          const header = bounds(inspectorSelector + " .operations-inspector-header");
          const tabs = bounds(inspectorSelector + " .operations-inspector-tabs");
          const body = bounds(inspectorSelector + " .operations-inspector-body");
          return {
            edgeCount: edges.length,
            visibleEdgeCount: edges.filter((edge) => edge.visible).length,
            edgeStyle: edges[0] || null,
            inspector,
            noWorkspaceOverlap: Boolean(content && inspector && content.right <= inspector.left + 1),
            noInspectorChromeOverlap: Boolean(header && tabs && body && header.bottom <= tabs.top + 1 && tabs.bottom <= body.top + 1),
            inspectorInsideViewport: Boolean(inspector && inspector.left >= 0 && inspector.right <= innerWidth + 1 && inspector.top >= 0 && inspector.bottom <= innerHeight + 1),
          };
        }, workflow.selection);
        if (layout.visibleEdgeCount < 1) addFailure("AgentRun: mind map has no visible connected SVG edges");
        if (!layout.noWorkspaceOverlap) addFailure("AgentRun: Inspector overlaps the workspace");
        if (!layout.noInspectorChromeOverlap) addFailure("AgentRun: Inspector header, tabs and body overlap");
        if (!layout.inspectorInsideViewport) addFailure("AgentRun: Inspector exceeds the viewport");
        const artifact = await captureScreenshot(workflow.selection.screenshotName);
        selection = { layout, screenshotSha256: artifact?.sha256 || null };
      }
    }
    const after = await agentObserverState(workflow);
    if (after.sourceCount !== before.sourceCount) {
      addFailure("AgentRun: view switch changed EventSource count " + before.sourceCount + " -> " + after.sourceCount);
    }
    if (workflow.blockedPhases.includes(after.phase)) addFailure("AgentRun: transport phase " + after.phase);
    return { before, after, views: viewResults, selection };
  }

  async function agentObserverState(workflow) {
    return await page.evaluate(({ eventSourcePath, phaseSelector }) => {
      const sources = Array.isArray(window.__unideskConsoleEventSources) ? window.__unideskConsoleEventSources : [];
      return {
        sources,
        sourceCount: sources.filter((url) => String(url).includes(eventSourcePath)).length,
        phase: document.querySelector(phaseSelector)?.textContent?.trim() || null,
      };
    }, workflow);
  }
}

function errorMessage(error) {
  return error instanceof Error ? error.message : String(error);
}
`;
}
