/** * Findings the explore loop derives from each action's observation delta — the freeze-less half * of the loop's value (#102). Everything here is a pure function over Evidence slices: mechanical, * deterministic, table-testable (the same stance as the critics). The LLM contributes only * `agent-note` findings; it never judges these. */ import type { ConsoleMessage, NetworkRequest } from "../types.js"; import type { Decision } from "../discover/decision.js"; export type FindingKind = /** a request the action fired failed for real (benign and recovered noise excluded) */ "failed-request" /** a console error surfaced after the action */ | "console-error" /** an effectful action ran ok but nothing observable changed — to a user, a button that does nothing */ | "dead-action" /** the action itself failed to execute (target didn't resolve, driver error) */ | "action-error" /** the page took longer than the threshold to quiesce after the action */ | "slow-settle" /** a UX problem the exploring model observed and recorded with a `note` decision */ | "agent-note"; export type FindingSeverity = "info" | "warn" | "error"; export interface Finding { kind: FindingKind; severity: FindingSeverity; /** What a reader needs to locate and reproduce the problem. */ detail: string; /** Identity for deduping repeats of the same problem across steps (an error a page throws on * every action) — stable across occurrences where `detail` may not be. */ key: string; /** Page URL where the finding was observed, when known. */ url?: string; /** Index into the report's `steps` of the action this finding is attributed to; for findings not * tied to an executed step (`agent-note`, `action-error`) the last executed step at the time. */ stepIndex: number; /** How many times the deduped problem occurred (absent = once); set by `dedupeFindings`. */ occurrences?: number; } /** What the loop records just before executing an action: the page URL, the lengths of the * append-only request/console logs (so the slice past them is exactly what the action caused), * and the element render (for the dead-action comparison). */ export interface ActionMark { url?: string; requestCount: number; consoleCount: number; render: string; } /** The completed observation after the action settled — cumulative logs plus the fresh render. */ export interface ActionOutcome { url?: string; requests: readonly NetworkRequest[]; console: readonly ConsoleMessage[]; render: string; /** Post-action settle wall-time; absent when the caller didn't time it. */ settleMs?: number; } export interface FindingOptions { /** URL substrings whose 4xx/5xx is product noise — mirror of `RunScenarioOptions.benign`. */ benign?: readonly string[]; /** Console-text substrings that are product noise — mirror of `RunScenarioOptions.benignConsole`. */ benignConsole?: readonly string[]; /** Settle wall-time at/above this is a slow-settle finding. */ slowSettleMs?: number; } /** * Derive the mechanical findings one executed action produced, from the delta between the mark * taken before it and the settled observation after it. Pure — the caller owns when to observe. */ export declare function deriveActionFindings(mark: ActionMark, outcome: ActionOutcome, decision: Decision, stepIndex: number, opts?: FindingOptions): Finding[]; /** Collapse repeats of the same problem (same `key`) into the first occurrence with an * `occurrences` count, keeping first-seen order — a page that throws the same console error on * every action is one finding, not thirty. */ export declare function dedupeFindings(findings: readonly Finding[]): Finding[];