Oroboros emblem Oroboro Labs
notes from the lab

The abort guard that lied

2026-08-30 · automation · 5 min

We run an agent that submits proposals on a freelance marketplace through raw Chrome DevTools Protocol. Before it clicks the final send button, an abort guard reads the form back and refuses to submit unless every field checks out: the price, the deadline, the confirmation checkbox, and a minimum length on the message. The guard exists so that the agent can never fire blind.

This week the guard did its job perfectly — and it was wrong.

What happened

The target project sat behind a 24-hour exclusivity gate, so the script polled the page until the gate lifted, then filled the form. The log read:

poll 39: exclusivo=False botao_visivel=True
abrir form: True
pre-submit: {}
ABORTED: form incomplete — nothing sent

Every field had just been filled. The guard saw none of it.

The bug

The guard was written as an arrow function expression and passed to Runtime.evaluate:

pre = pg.ev("""()=>[document.querySelector('#oferta')?.value,
  document.querySelector('#confirmar-envio-proposta')?.checked, ...]""")

evaluate runs an expression. A bare arrow function is a perfectly valid expression — its value is a function object. Serialized with returnByValue, a function becomes {}. The guard didn't fail to run; it ran to completion and returned the function itself, un-invoked. One missing pair of parentheses.

pre = pg.ev("""(()=>[...])()""")   # invoke it

Fixed and re-run: pre-submit: ['900', '10', True, 1356] — guard green, send fired, the marketplace's own "cancel / improve proposal" controls confirmed the submission, and the panel now lists the proposal with the exact price and duration the guard verified.

Why this one matters

The rule we took

Any guard whose expression is handed to an evaluator gets its result asserted against a positive control once, in a dry run, before it is trusted to gate a real action. If your verification step has never returned "go" on a state you knew was good, you don't have a guard — you have a coin that only ever lands on "no".

Measured data from this incident: the 24-hour exclusivity gate opened at ~09:35 local, 39 polls (~39 min) into a poll loop started at 08:56 — consistent with, and slightly later than, the ~24h-after-publishing clock measured in an earlier post. All figures are from our own logs; nothing here is sponsored or affiliated with any marketplace.
Lab output & tools