Skip to content

flow: count a self-closing popup click as delivered only when the step expects it - #1321

Merged
abrichr merged 2 commits into
mainfrom
claude/popup-self-close-delivery
Oct 19, 2026
Merged

abrichr merged 2 commits into
mainfrom
claude/popup-self-close-delivery

Conversation

@abrichr

@abrichr abrichr commented Oct 19, 2026

Copy link
Copy Markdown
Member

A popup can close itself from the click that finishes it, such as an approval or sign-in window. Playwright can report "Target page, context or browser has been closed" after it delivered that click. Before this change:

Now that report counts as a delivered click only when the step expects its popup to close. Every other "page closed" report stays uncertain.

Rule

PlaywrightBackend (click, act_structural, act_guarded_coordinate) treats a TargetClosedError as delivered only when all of these hold:

  1. the step armed an expected close (expect_page_close(), one-shot);
  2. the backend is a popup surface (closed_page_frame=True, a page a step opened, per feat: multi-tab, popup, and backend-switching surfaces (carry-over of openadapt-flow#533) #1197);
  3. the page was open when the click started;
  4. the page reads as closed within 1 s.

Otherwise it raises ActionDeliveryUncertain. Other errors are unchanged.

Where the expectation comes from

  • Recorder: a click that leaves its popup surface closed is recorded as closes_surface: true. This covers driven and observed (interactive) recordings, and is checked after the settle wait because the close is asynchronous. While delivering a click on a popup, the recorder arms the expectation, so recording continues past the report.
  • IR: new field Step.closes_surface. It is valid only on a click or double-click that runs on a page a step opened. A False value is omitted from the sealed v2 digest (same rule as opens and dialog), so existing bundles keep their digests.
  • Replayer: _act arms the backend for a closes_surface step only, and cancels the expectation afterwards. The new seam is PageCloseExpectationBackend in backend.py.

Tests

  • tests/test_page_close_delivery.py (13 tests):
    • backend: expected close counts as delivered; unexpected close stays uncertain; the primary page is never accepted; a page still open stays uncertain; a page already closed before the click stays uncertain; the expectation is one-shot; a cancelled expectation is ignored; other errors are passed through unchanged;
    • IR validation;
    • the replayer arms only for a closing step.
  • tests/test_multi_surface_selfclose_delivery.py (2 tests, real Playwright on the fixture's self-closing sign-in popup): every popup click is forced to raise TargetClosedError after it is delivered.
    • Recording continues and marks the closing step.
    • The replay counts it as delivered and succeeds.
    • The same bundle with closes_surface cleared records delivery_uncertainty with cause TargetClosedError.
  • tests/test_bundle_schema_v2.py: pre-field digests are preserved.
  • Locally, the multi-surface, recorder, compile, Playwright backend and bundle suites pass. Ruff, format, the strict mypy subset and release consistency are clean.

This is a new file, so it doesn't conflict with #1283's changes to test_multi_surface_selfclose.py, which cover the test's direct popup.click.

🤖 Generated with Claude Code

abrichr and others added 2 commits October 18, 2026 20:32
A popup can close itself from the click that finishes it (an approval or
sign-in window). Playwright may then report "Target page, context or
browser has been closed" after delivering the click. The recorder raised,
and replay recorded the step's delivery as uncertain.

- The recorder marks a click that left its popup surface closed as
  Step.closes_surface (driven and observed recordings), and expects that
  close while delivering a click on a popup.
- The replayer arms PlaywrightBackend.expect_page_close() for that step
  only (new PageCloseExpectationBackend seam), and cancels it afterwards.
- PlaywrightBackend.click, act_structural and act_guarded_coordinate count
  a TargetClosedError as delivered only when the close was expected, the
  page is a popup surface (closed_page_frame), it was open when the click
  started, and it reads as closed within a second. Any other
  TargetClosedError is ActionDeliveryUncertain; other errors are unchanged.
- closes_surface is valid only on a click on a page a step opened; a false
  value is omitted from the sealed v2 digest, so existing bundles keep
  their digests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abrichr
abrichr merged commit 4594782 into main Oct 19, 2026
38 checks passed
@abrichr
abrichr deleted the claude/popup-self-close-delivery branch October 19, 2026 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant