/** * Freeze-time target stability scoring (#14). A frozen target's locators decide how well replay * survives UI change: a text-only target breaks on a rename and forces a `self-heal` (LLM re-entry * — cost + non-determinism); a `selector` or a `role`+`index` fallback survives it. Scoring and * warning at freeze time lets the author strengthen weak targets up front, lowering the self-heal * trigger rate. Pure — no I/O, no LLM (invariant #4). */ import type { Scenario, Step, Target } from "./types.js"; import type { TraceEvent } from "./trace.js"; export interface TargetScore { /** 0..1 estimate of how well the target survives UI change. */ score: number; /** below the warn threshold — likely to force a self-heal later. */ weak: boolean; reason: string; } /** Score how well a single frozen target survives UI change. */ export declare function scoreTarget(target: Target): TargetScore; /** A step's target paired with its stability score. */ export interface ScoredTarget { stepIndex: number; step: Step; target: Target; score: TargetScore; } /** Score every located step in a scenario — caller decides what to do with the weak ones. */ export declare function scoreScenario(scenario: Scenario): ScoredTarget[]; /** The weak targets in a scenario — flag these at freeze time so the author can strengthen them. */ export declare function weakTargets(scenario: Scenario): ScoredTarget[]; /** A run of blind key presses discover emitted instead of resolved click/type targets (#61). */ export interface GuessedKeyRun { startIndex: number; keys: string[]; } /** Flag blind pressKey chains at freeze time: a Tab anywhere, or ≥2 consecutive key presses, is * focus-guessing — it can land on the wrong element (or nothing) and still pass coarse assertions * (#61). A single non-Tab press (an Enter submit after typing) is a normal pattern and not flagged. */ export declare function guessedKeyRuns(scenario: Scenario): GuessedKeyRun[]; /** * Does this freeze carry a check that only the flow can satisfy, and that the freeze itself can * stand behind? Two kinds qualify: a live `request-status`, grounded against a request the run * actually made, and a `custom` check the product registered and judges with its own code. * * `expect` is deliberately NOT counted. A semantic criterion is a sentence the model wrote: it is * never grounded against the evidence, and `markVacuous` cannot judge it either, so the freeze has * no basis for claiming it verifies anything. It may well be the real check of the flow — an LLM * critic judges it at replay — which is why the warning names it instead of ignoring it. * * It still over-warns on a genuinely read-only flow, which has no action to prove; that is the * loud direction and is left as is. */ export declare function provesAnAction(scenario: Scenario): boolean; /** Did the freeze keep a semantic criterion? Judged by an LLM at replay, never grounded here. */ export declare function hasSemanticCriterion(scenario: Scenario): boolean; /** * The reason a grounding gate dropped a proposed `request-status` — or `undefined` for any other * event. These are the lines worth reading next to a freeze that ended up with no proof: the most * common is a check the model invented that no captured request matched, and the rest are the * freeze refusing a value it could not make replayable (no stable path, a widening that would * match a different endpoint). Drops of other kinds — an `expect` without `--semantic` — stay * trace-only. * * A dropped proof does not fail the scenario by itself: a surviving `navigated` still passes it. * Whether to warn is therefore decided by what the freeze CARRIES, not by what it dropped; these * reasons only explain a freeze already found wanting. */ export declare function droppedProofReason(event: TraceEvent): string | undefined;