You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Element refs (E1, E2, …) are positional counters minted fresh on every snapshot; they are not stable across snapshots and can go stale between the model reading a snapshot and the action firing. On a churny SPA this surfaces as Invalid element reference errors.
Surfaced by a Zoo eval (Magento guest-checkout): stale-ref rate was ~3% of actions (~1 per checkout) — occasional, but before #625 a single stale ref was deterministically fatal: with no snapshot refresh on the error, the model kept acting on a frozen snapshot, confabulated refs, and hit the consecutive-error budget. Evidence + forensic write-up live in the eval repo at docs/dev-sessions/2026-07-27-vwa-checkout-stale-ref/.
#625 adds loop-level recovery: on a stale/invalid-ref error, force a snapshot refresh so the model gets live refs and retries. Validated 11/11 on the checkout task (each recovering from its stale ref). This stops the cascade but (a) costs a model round-trip per event and (b) doesn't reduce the underlying rate.
Possible general improvements (leverage order)
Browser-layer self-healing(highest leverage) — on an invalid ref, re-resolve the element by its stored identity before erroring to the model. Pilo already captures {role, name} per ref (__piloIdentityMap) and sets data-pilo-ref on the DOM. If the same element still exists, act transparently — most stale refs become silent successes, no LLM round-trip.
#446 (search_page/find_elements) gives a stable-descriptor → current-ref addressing path (CSS/text → live nearestRef), which is more robust than counter refs and a cleaner recovery route than confabulation — but it's model-driven and its nearestRef still depends on data-pilo-ref freshness (re-minted only on snapshot), so it composes with #625 rather than replacing it. Its plumbing (closest('[data-pilo-ref]'), live querySelector) is exactly what option 1 would build on.
Recommendation
At ~3%, #625's recovery is right-sized. Measure stale-ref rates across sites first (the partial cloud eval on the #625 build will help) before investing in option 1 — build the self-heal only if rates elsewhere justify it.
Context
Element refs (
E1,E2, …) are positional counters minted fresh on every snapshot; they are not stable across snapshots and can go stale between the model reading a snapshot and the action firing. On a churny SPA this surfaces asInvalid element referenceerrors.Surfaced by a Zoo eval (Magento guest-checkout): stale-ref rate was ~3% of actions (~1 per checkout) — occasional, but before #625 a single stale ref was deterministically fatal: with no snapshot refresh on the error, the model kept acting on a frozen snapshot, confabulated refs, and hit the consecutive-error budget. Evidence + forensic write-up live in the eval repo at
docs/dev-sessions/2026-07-27-vwa-checkout-stale-ref/.What #625 already does
#625 adds loop-level recovery: on a stale/invalid-ref error, force a snapshot refresh so the model gets live refs and retries. Validated 11/11 on the checkout task (each recovering from its stale ref). This stops the cascade but (a) costs a model round-trip per event and (b) doesn't reduce the underlying rate.
Possible general improvements (leverage order)
{role, name}per ref (__piloIdentityMap) and setsdata-pilo-refon the DOM. If the same element still exists, act transparently — most stale refs become silent successes, no LLM round-trip.networkidle/ "tree stopped growing").Relationship to #446
#446 (
search_page/find_elements) gives a stable-descriptor → current-ref addressing path (CSS/text → livenearestRef), which is more robust than counter refs and a cleaner recovery route than confabulation — but it's model-driven and itsnearestRefstill depends ondata-pilo-reffreshness (re-minted only on snapshot), so it composes with #625 rather than replacing it. Its plumbing (closest('[data-pilo-ref]'), livequerySelector) is exactly what option 1 would build on.Recommendation
At ~3%, #625's recovery is right-sized. Measure stale-ref rates across sites first (the partial cloud eval on the #625 build will help) before investing in option 1 — build the self-heal only if rates elsewhere justify it.
🤖 Generated with Claude Code