TL;DR: Wait for the modal to be both attached and visible before clicking, and re-query the element if it detaches mid-click. The modal renders asynchronously after the click that opens it, so clicking too early targets a node that is not in the DOM yet. An explicit wait plus a fallback click through page evaluation fixes it.

```text
puppeteer click failed when agent tried to submit review: confirmation modal not attached to DOM
```

1. Reproduce with a screenshot or DOM dump captured at click time to confirm the modal was missing or hidden when the click ran.
   Expected: the dump shows no visible modal element at the moment of the click.
2. Before clicking, wait for the modal selector with both attached and visible states and a generous timeout.
   Expected: the wait resolves only when the modal is actually in the DOM and visible.
3. Scroll the modal's button into view before clicking.
   Expected: the button's bounding box is within the viewport.
4. Perform the click. If Puppeteer throws a detached-node error, re-query the selector for a fresh element handle and click again instead of reusing the stale handle.
   Expected: the second attempt clicks a live node and the review submits.
5. As a fallback, dispatch the click through page evaluation on the selector, which works even when the element is covered by an overlay for a frame or two.
   Expected: the review submits and the modal closes.
6. Assert the post-click state (modal closed, review-posted indicator present) rather than assuming the click landed.
   Expected: confirmation UI appears. If it does not, the step reports a failure instead of silently passing.

## Use this when
- a Puppeteer-driven agent clicks a confirmation modal that renders after a delay
- the error mentions a detached node, a missing element, or a not-clickable target
- the same click works when retried manually a moment later

## Not for this skill when
- the modal never appears at all (the triggering click probably missed - check that step first)
- the click fails because of an iframe boundary (that is a frame-targeting problem)
- the button is permanently covered by another element (that is a layout problem, not timing)

## Variant phrasings
### puppeteer cannot click modal that loads after a delay
### confirmation dialog click fails: node is detached from document
### review submit button not clickable: modal not rendered yet

## Why it happens
GitHub renders confirmation modals asynchronously after the click that opens them. An agent script that fires the follow-up click in the same tick runs before the framework has attached the modal node. A handle grabbed before attachment points at nothing, and a handle grabbed during a re-render can detach between the query and the click.

## Edge cases
- The modal may attach but stay invisible (opacity 0) for an animation frame; require visible, not just attached.
- Focus traps in modals can steal focus from the button; the click still works but keyboard-based fallbacks may not.
- Headless versus headed rendering timing differs; a wait that passes locally can flake in CI, so keep timeouts generous.
- If the modal content loads data asynchronously, the button may shift position after attach; wait for stability, not just presence.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_Bc_p140OZ7DVAV6ZBndGPA
