For the agent driving it

Wired by the repository, not by each machine

Adoption that depends on somebody remembering a per-machine install is adoption that stops at the person who set it up. winwright ships as a Claude Code plugin: two commands in the repository wire it, every clone is wired, and nothing is added to any path.

The friction this removes

What a hand-rolled harness costs, and what replaces it

None of this is a defect in the tools it names. Every one of them is what a general-purpose automation library does to somebody who has to answer, at the end of a run, whether the thing they were checking was actually checked.

A pass and a check that never ran are the same colour
the script you have
Assert.True(found is not null)
A precondition that was absent throws, is caught, and turns into a skip nobody counts — or worse, into a branch that returns early and reports nothing at all. The total moves and the colour does not.
here
winwright → Degraded (2)
The unevaluated reading is named in the summary, and the process exits 2 — a number CI can act on without anybody reading a word of the output.
The locator is a different string in every verb
the script you have
FindFirst(TreeScope.Children, new AndCondition(...))
A condition tree is written per call site, so the same control is addressed four different ways in four places, and the one that breaks is the one nobody recognises as the same control.
here
Window#main > Pane > Button#save
One grammar, read the same way by every verb, refused at parse time with the position and the reason rather than at run time with a null.
Every act is a click, so every act needs the screen
the script you have
SetForegroundWindow(h); MoveTo(x, y); Click()
Which means the run owns the desk, a notification that steals the foreground is a failure, and the same script cannot be run on the machine somebody is working on.
here
act.Invoke(subject)
A pattern act asks the control through its own accessibility peer and needs no foreground. The verbs that do synthesise input are marked as such in the catalogue rather than discovered on a red run.
The expected string is typed into the test
the script you have
Assert.Equal("Relatório mensal", label)
A second copy of the truth, and it is the copy that goes stale — silently, on the day somebody edits the language file and nobody edits the test.
here
expect.LabelsFrom(project.Language)
The expected set is derived from the application's own language files, so switching the resolved language switches the expectation with it, and a key none of the files carries refuses the run rather than matching nothing.
A screenshot is written whatever it contains
the script you have
CopyFromScreen(bounds); bmp.Save(path)
A dialog over the window, a backdrop transmitting what is behind it, a page still computing, or nothing rendering at all — each writes a file, and each exits zero.
here
capture → refused: “Toast (App.exe) covers 240×64 of the rectangle”
Every way the picture can lie is checked, and the refusal names the intruder, its process and the rectangle rather than cropping around it.
A case is two hundred lines that mostly repeat the previous case
the script you have
// twenty-seven copies of one runner
The loop, the waits and the verdict logic are re-authored per case, so a fix to any of them is applied to some of the copies and the rest keep the bug.
here
winwright run cases/report.wwx --case language-round-trip
Steps, locators, acts and expectations are fields; the loop, the waits and the verdicts belong to the engine. What is left in the file is the part that is about your application.

8 tools, and every one of them takes this project's own scenario schema.

Which is the difference between a refusal and a guess: an agent that writes a case field by field is corrected at insertion, where the fix costs a retry, rather than at run time, where it costs a red run somebody has to read.

read — inspection (no prompt)
read treereads
the control view under a window or an element, as a tree or as lines a person reads
read factsreads
what UI Automation says about one element, and what each of its patterns reads
read locatorreads
parse a locator, or say where and why it did not — before a run spends a minute on it
read verdictreads
the last run's summary: what held, what did not, and what was never evaluated
read tracereads
the step behind any result that answered a verdict, in the order they happened
run — driving the application (still asks)
run casedrives
one case, or one file, or one tag — and it says what it did not run
run preflightdrives
what each declared act needs, checked against the tree before anything is pressed
run capturedrives
a picture, with every refusal that makes it evidence rather than a file

The one line that pays for it

One entry in .claude/settings.json:

Bash(winwright read:*)

read is a promise, not a naming convention: a verb under it that drives the application is a defect. So this single grant removes every prompt on the inspection path — the tree, the facts, the verdict of the last run — while everything that presses a control still asks.

The hook that keeps the shortcut closed

A hand-written harness script is the path of least resistance, and it is how every repository this framework was extracted from ended up with two of them. So a hook denies one and names the verb that replaces it — the same guard roadkeep puts in front of a governed file, pointed at the same failure mode.

The skill that is not always loaded

It loads when a window is in play rather than on every turn, and what it says is which loop answers which question — which is the whole of what an agent needs in order to reach the right verb instead of the nearest one.

What it deliberately refuses

winwright is the substrate; the intelligence is the caller's. It reads trees, presses controls through their own patterns and assembles a verdict — it does not decide what is worth asserting.

No model

It calls no LLM.

No prompts

It stores none.

No recorder

Clicks never become a case.

No invented assertion

The tool never writes the test.

No second desk

It drives the one you are on.

No telemetry

Nothing is measured or sent.