One day the browser automation flow started failing right after plugin updates with NameError: name 'plugin_form_selectors' is not defined in the post-update "residual check" step.

The refactor that introduced this had landed back in v1.6.1. The error didn't surface until many rounds later. Reading the code, the cause is obvious in seconds — but nobody hit it for ages, because Python's lazy evaluation kept the leftover reference hidden until exactly the right execution path ran. This post walks through what the bug was and how we structurally prevented its kind via an AST static-analysis test.

What happened — a reference that crossed a scope boundary

browser_utils.py has two functions involved: run_browser_update_flow(), which orchestrates the whole update flow, and browser_update_remaining_plugins(), which handles only the plugin-update logic. The list of plugin-form selector candidates, plugin_form_selectors, used to be a local variable inside run_browser_update_flow().

In the v1.6.1 refactor — "let's split plugin update into its own function" — we created browser_update_remaining_plugins() and moved the plugin_form_selectors definition into it.