Three bugs in the version-based browser recycling caused requests to
hang after ~80-130 pages under concurrent load:
1. Race condition: _maybe_bump_browser_version() added ALL context
signatures to _pending_cleanup, including those with refcount 0.
Since no future release would trigger cleanup for idle sigs, they
stayed in _pending_cleanup permanently. Fix: split sigs into
active (refcount > 0, go to pending) and idle (refcount == 0,
cleaned up immediately).
2. Finally block fragility: the first line of _crawl_web's finally
block accessed page.context.browser.contexts, which throws if the
browser crashed. This prevented release_page_with_context() from
ever being called, permanently leaking the refcount. Fix: call
release_page_with_context() first in its own try/except, then do
best-effort cleanup of listeners and page.
3. Safety cap deadlock: when _pending_cleanup accumulated >= 3 stuck
entries, _maybe_bump_browser_version() blocked get_page() forever
with no timeout. Fix: 30-second timeout on the wait, after which
stuck entries (refcount 0) are force-cleaned.
Includes regression test covering all three bugs plus multi-config
concurrent crawl scenarios.