}

Detect a portal change before it costs you data: the browser agent's expectation check

By
Bodo Buschick
•
6 min
Detect a portal change before it costs you data: the browser agent's expectation check

"How do you detect a portal change?" Before we notice missing data. That question comes up a lot. Usually on day two of onboarding. Right when a dispatcher sees what a browser agent inside a customer portal actually does. My answer is rarely the expected one. Not better selectors. An expectation check. It asks, at every step: is the page still the one from yesterday?

My position: a portal without an API changes without warning. The operator swaps a layout. Moves a column. Adds a new cookie banner. The agent does not notice on its own. It only notices whether its code still runs. And running code almost always reports success. Even with no result. Or the wrong one.

Two ways an agent stays green after the change

The Logistics Benchmark 2025 checked 54 logistics companies. 55 percent offer a customer portal. Only 20 percent of those deliver real functional value. The rest is a screen. Maintained from the outside. With no regard for the agent visiting it daily.

Case one: the portal shows an empty list. Does that mean "nothing to do"? Or does a new cookie banner cover the table? A naive agent cannot tell. Both cases return zero rows. Both exit with code 0.

Case two is worse. The portal swaps two columns. Quantity and weight trade places. The agent keeps reading column three. It gets a number. Just the wrong one. No error, no warning. Only a value that looks plausible.

The expectation check on every step

Both cases need the same answer. A step must not just run. It must check its own result. Four checks cover most portals.

Is the expected element there at all? Not just "the page loaded." But: the element with the meaning result table exists.

Does the expected column header sit in the header row? Not at position three. Found by its own text.

Is the row count plausible, measured against the median of recent runs? Three rows instead of eighty is not proof. Only a reason to check.

Are the required fields filled? A row with no reference number is not a row. It is a parsing error with a checkmark in front.

At one customer, a header check like this ran unnoticed for months. 4,869 runs between early June and mid September. 4,850 of them successful. A rate of roughly 99.6 percent. On 23 July, between 5:30 and 7am, the check escalates four times in a row. From a warning to an error. Reason, machine-readable: two expected column headers are missing from the first eight rows. The message names the likely cause itself: someone is editing the file right now. Half an hour later the next check ran clean again. Without a single human. The check had classified the glitch correctly. No alert for dispatch. Just a wait signal for itself.

A structural fingerprint instead of a pixel comparison

A screenshot comparison sounds obvious. It fails at every ad banner. Every A/B variant. Every screen width. The better path: a structural fingerprint of the page. Not what it looks like. What it is built from. Row count in the table. Order of the form fields. A hash over the relevant DOM nodes, with ads and timestamps stripped out.

Current approaches in web scraping split this into three tiers. Full-text hashing. Filtered DOM hashing. Element-level hashing for single fields. For a customer portal, the middle tier is usually enough. A hash over the structure the agent actually touches. If the hash changes for no good reason, that is a signal. Not proof. Just a signal that makes the next run more careful.

Idle with a reason, not just idle

Zero results are not an error. And not a reason to relax. They are an open question. The agent answers it itself, with one of two reasons. "Queue-empty" means: nothing is due today. The process behind it is empty. "Input-missing" means: the portal is not delivering what is expected. Even though something should be there. The first reason needs no human. The second needs one, fast. Without this split, both cases land in the same green line. With it, only one does.

Kill switch, screenshot and the fix in test mode

A single failed attempt is no proof. Networks stutter. Servers answer slowly sometimes. The kill switch only fires after a fixed number of failures. Not the first one. On the first deviation, the agent still writes down two things: a screenshot of the page. And the DOM excerpt of the spot where the check failed. That is the gap between two hours of searching and a five-minute read.

The excerpt becomes the fix: a new selector, a new expected column header. It never rolls out straight to the live run. First in test mode, against the last saved state of the portal. Only once the test meets the new expectation does it go live. The kill switch stays on until then. A paused process beats one that treats a wrong number as real.

What the dispatcher sees in the monitor

On a normal day, the dispatcher sees nothing at all. Green, line by line, as expected. On the day the portal really changes, they see a paused process. With a plain-language reason. Not "error 500." But "expected column header missing, screenshot attached." That is the gap. An outage someone catches fast. Or one that writes wrong numbers for three days straight.

For how a single healthy run gets logged step by step, see How A Browser Agent Runs In The Customer Portal. For why a portal without an API is not a blocker in the first place, see A customer portal without an API is not a blocker. And for why a clean run with no result is an outage, for any automation, see "The job ran" is not proof of health. This post stays with the detection itself.

Where this breaks

Three honest limits.

Some portals randomize their structure per session. For security reasons. Or a frontend framework that renames every class on every build. A fingerprint fires there constantly, for nothing. Then only the content check is left: column header and required field, no hash.

A fingerprint that is too sensitive creates its own idle case. Every harmless A/B variant pauses the process. The threshold needs calibration. Not a one-time setting.

And the hardest limit: the check only finds what it expects. Does the portal change a field's meaning? Without changing its format? "Done" becomes "in progress," same data type. The check sees nothing wrong. That is not a flaw in the technique. That is the limit of every automated expectation. It checks what it already knew to check.

Tools

Four expectation checks per step. A structural fingerprint over the relevant DOM nodes. Two idle reasons instead of one silent nothing. A kill switch after a fixed number of failures, with a screenshot and a DOM excerpt on the first deviation. A fix that runs in test mode first, then live. None of this is exotic. What is new is that it exists for every single customer portal. Not just for your own system.

Which of your portals changed its layout without anyone noticing? Tell me, even "no idea" counts. I will show you in a process check what an expectation check looks like for that exact portal.