Repository navigation
bench: checkout intents graded from the page, with a submission guard - #118
Merged
yummybomb merged 1 commit intoOct 9, 2026
Merged
Conversation
yummybomb
force-pushed
the
hypeship/bench-checkout-guard
branch
from
October 8, 2026 20:33
d141b95 to
0928c6a
Compare
yummybomb
force-pushed
the
hypeship/bench-checkout-guard
branch
from
October 9, 2026 02:25
0928c6a to
13c24bd
Compare
A prepare intent can walk a site's flow, or a checkout up to the button that pays, and is graded from the page the agent left, read the same way in both arms. Domains list state-changing `actions` and `checkout_actions` for those intents. An intent that allows checkout gets a guard extension in both arms that blocks and counts submit attempts; a trial with any is crashed, and one whose guard count or end state cannot be read aborts. Intents can carry their own profile and a rule-based sign-in check, and `opt_in` keeps an unrehearsed intent out of default runs. Adds Blue Bottle and Delta cart and checkout intents.
yummybomb
force-pushed
the
hypeship/bench-checkout-guard
branch
from
October 9, 2026 06:09
13c24bd to
545bda0
Compare
yummybomb
marked this pull request as ready for review
October 9, 2026 08:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #117. Folds in #115's checkout intents and #120's page-based grading, so the end-state check is reviewed once, in its final form.
Summary
Lets bench intents walk a site's flow part way, or through a checkout up to the button that pays, and grades where the agent left the browser from the page itself. Adds three such intents.
Prepare intents. The bench so far only measured single lookups with read-only tools, so it could not measure multi-step flows such as booking a flight, where tools should help most. A
prepareintent has acheck, which the runner applies to the page the agent left, in both arms, never through the tools under test:rulessee the page'surl,titleand visibletext;extractmaps names to regexes whose first group is read from that text asextracted.<name>, with amounts parsed as numbers;fieldsmaps names to CSS selectors of inputs, read asfields.<name>, withmissingandinvalidlists, for pages such as a third-party checkout form;same_as_answerlists{answer, end_state}path pairs that must match, so the answer reports what is actually on the page.The page is read through Playwright on Kernel, or the DevTools protocol locally, and saved as
end-state.json.Actions. A domain's
domain.jsonlistsactions, tools that change site state, each with a reason, registered for itsprepareintents; andcheckout_actions, registered only for intents that allowcheckout. Delta'sactionsareselect_fareandselect_fare_experience; its seat, Review and Pay and trip protection tools arecheckout_actions, so the cart-only intent never gets tools that go past Trip Summary. Blue Bottle's areadd_to_cart, andcontinue_to_checkoutfor checkout. Tools that sign in, pay, book or send stay out.Policy. The system prompts keep "never pay, place an order or complete a booking" absolute and change the other two rules to "unless the task says you may": carts and checkout, and entering personal or payment details (only the ones the task gives). A
prepareintent opts in withallow:checkout: add to the cart and go through checkout up to, never including, the button that pays or books;payment_details: enter the details the task gives, which must be test data.The runner adds the same fixed sentences to both arms' prompts.
Guard. An intent that allows
checkoutmust have aguard: hosts, a button-text regex, and optionallyselectors,form_submitand a request regex. The runner builds it into a small Chrome extension (named by a hash of its content, uploaded once and reused) and loads it in both arms' browsers; locally it loads as a second unpacked extension. Its script runs in the page's own world, in every frame, from document start, so it survives navigation, unlike an init script added throughplaywright execute, which I checked does not. It blocks clicks on buttons whose text matches, or on anything inside an element matchingselectors, and, withform_submit, a form's submit buttons, submit events,form.submit()/requestSubmit()and Enter in form fields. Its declarativeNetRequest rules block requests matchingrequestsfrom any frame, and withform_submit, POST navigations started on the guard's hosts. That covers Playwright'spage.evaluate, which runs in an isolated world where the page-world overrides are not installed. Blocked clicks and submissions are counted in a cookie on the page's host; the runner reads the cookies of the whole browser after the agent, so an attempt counts even if the agent then left the page, and a trial with any iscrashed("tried to submit"). This follows the espresso runner's submission guard and is defense in depth: checkout intents also use a test card or a test account with no saved card.Signed-in intents. An intent can carry its own
profileandsigned_in_check, so one signed-in intent doesn't force a profile on the domain's other intents. Asigned_in_checkcan also takerules, for a tool that reports signed-out state instead of returning an error.Intents.
delta-cart-main-classic: put the cheapest Delta Main Classic one-way fare in the cart and stop on Trip Summary, graded from Trip Summary's text: the route and One Way header,Cabin class/Cabin Nameequal to Main Classic, 1 passenger, and Amount Due matching the answer. It reads both Trip Summary variants Delta serves (/purchase/flights/and/completepurchase/).bluebottle-checkout-espresso: the espresso task. One bag of Opascope Espresso as a one-time order; checkout filled with fictional details and test card 4242 4242 4242 4242; all ten checkout fields read from the page and checked. Checkout's nativefill_payment_formbelongs to the site, so both arms can use it.delta-checkout-signed-in: on thebench-deltaprofile, book the cheapest Main Classic one-way fare up to Review and Pay's Payment section (fare, Trip Summary without seats, prefilled passenger details, trip protection declined), checked from the page's text and its Amount Due. The sign-in check isget_page_contextwithsigned_in: true.Also:
fill_payment_formreturnsawaiting_submission, its success state when it doesn't submit, whichinvoke okand the tool failures table counted as a failure.Testing
node --test "bench/**/*.test.mjs": 92 pass on Node 26. New tests cover a trial whose guard count or end state cannot be read, which aborts (it used to be graded as if nothing was blocked, orwrong), sign-in check rules validated like intent rules,opt_inintents, and validation ofcheck(onlyfields,extract,rulesandsame_as_answer; one capture group per extract pattern),allow,guardand intent-level profiles,actionsandcheckout_actionsreaching the right intents, the prompt sentences, the guard extension's manifest and content-hash name, page-text extraction (including a group that takes no part in the match), page-based grading andsame_as_answer, grading the espresso intent against page states (formatted card, expiry and phone values; wrong card; invalid field; wrong URL), andawaiting_submission. The stub run test covers a guarded checkout intent: the extension is uploaded once and loaded for the smoke gate and both arms, a trial whose guard blocked an attempt iscrashed, and a second run reuses the extension. Dropping the extension from browser creation, or ignoring the count, fails it.Paybutton andform.requestSubmit()were blocked and counted, Enter in the email field was blocked and not counted, and the page stayed on checkout.evaluate, locally in headless Chromium 154: the guard loaded next to the xwebmcp extension;Payand a form's submit button were blocked; aPay with cardbutton outside a form was not.delta-cart-main-classic, pi withopenai/gpt-5.4-mini: both arms reached Trip Summary with DL1150, Main Classic, $173.40 (treatment 61s, control 243s), but my first amount regex missed. The saved page text showed two Trip Summary variants and that "Main Classic" also appears in the Comfort upsell; the check now reads the cabin line, and regrading both saved pages givescorrect. A second run: treatmentcorrectin 86s; control hit the turn cap after 409s on the results page.get_trip_summary(since replaced by the page check): 3 pairs on Haiku 5.5 at low thinking, all 6 trials reached Trip Summary with DL 1162, Main Classic, ATL→MCO, $143.40 in the cart; treatment was faster in all 3 (1.3×).bluebottle-checkout-espresso, pi withopenai/gpt-6-lunaat medium thinking, 2 pairs: all 4 trialscorrect, every field verified, no submit attempts. Treatment usedfind_product→get_product→add_to_cart→get_cart→continue_to_checkout→fill_payment_formand finished in 0:59 and 1:18; control in 1:51 and 1:47, filling the form with Playwright. About $0.04. Re-run withopenai/gpt-5.4-mini, 1 pair: both armscorrect, 0 blocked attempts; treatment 0:43, control 5:34.get_page_contexton www.delta.com in a signed-out Kernel browser returnssigned_in: false, so the Delta sign-in check catches a lapsed session.delta-checkout-signed-in. It needs abench-deltaprofile on a Delta test account that cannot pay: no saved card, zero SkyMiles, and no eCredits, gift cards or certificates (see "Signed-in trials" in the README). It isopt_in, so default runs skip it even once the profile exists, and only--intent delta-checkout-signed-inruns it (stopping if the profile is missing). Rehearse it once on the live site after the profile is created, then dropopt_in. Its guard'sselectorsand its page check are also untested on Delta's live Review and Pay.Guard fixes
context.cookies()on Kernel andStorage.getCookieslocally. Checked on a Kernel browser: a blocked click on example.com still read 1 after navigating to iana.org.form.submit()called frompage.evaluatesubmitted the form despiteform_submit, andfetchto arequestsURL frompage.evaluatewent through, because Playwright evaluates in an isolated world.requestsis now a declarativeNetRequest rule (validated as RE2: no lookarounds or backreferences), andform_submitadds one blocking POST navigations from the guard's hosts. Re-checked on Kernel: both are blocked; a GETform.submit()still goes through. Neither network block is counted.[data-cy^="complete-purchase"](the Complete Purchase, PayPal and Apple Pay buttons), and its text regex matches a label that starts with the button's name rather than equals it, soComplete Purchase $143.40is caught. It still has noform_submit, since Review and Pay's Save & Continue buttons may be form submits. Checked in headless Chromium against a page with those buttons; not yet checked on Delta's live Review and Pay.