Observed while dogfooding Bluesky (2026-07-04, main + #1075 fixes):
snapshot -i → @e37 = the post body text ("Pretty sure this is the first award…", y≈245).
- One
tap "label=..." in between (its resolution capture refreshed the session snapshot; the feed also loaded new content).
get text @e37 → success, returning "2.7K likes" from a like-count StaticText at y≈847 — a completely different node, no warning.
Same session, second occurrence: press @e69 (was the Search tab at y=793) failed with offscreen_ref because e69 now pointed at a node at y=1108 — the guard caught it only by luck of geometry.
Refs are positional indexes into the latest session tree, and any selector-based command silently reshuffles them. STALE_REF handling exists for refs that vanish, but a ref that still exists pointing at a different element is a silent wrong answer — for an agent this is worse than an error, since it corrupts downstream reasoning (reading the wrong text, pressing the wrong control).
Expectation (design discussion needed): the session could stamp each snapshot with a generation counter and remember which generation served the refs the client last saw; commands taking @ref args could then either (a) warn in the response when the tree was replaced since that generation, or (b) compare the resolved node's label against the recorded refLabel for that ref and refuse/warn on mismatch (the replay fallbackLabel machinery already does something similar). Zero-cost paths should stay zero-cost; even a response-level refsMayBeStale: true marker after a tree-refresh would let agents re-snapshot instead of trusting garbage.
Observed while dogfooding Bluesky (2026-07-04, main + #1075 fixes):
snapshot -i→@e37= the post body text ("Pretty sure this is the first award…", y≈245).tap "label=..."in between (its resolution capture refreshed the session snapshot; the feed also loaded new content).get text @e37→ success, returning"2.7K likes"from a like-count StaticText at y≈847 — a completely different node, no warning.Same session, second occurrence:
press @e69(was the Search tab at y=793) failed withoffscreen_refbecause e69 now pointed at a node at y=1108 — the guard caught it only by luck of geometry.Refs are positional indexes into the latest session tree, and any selector-based command silently reshuffles them.
STALE_REFhandling exists for refs that vanish, but a ref that still exists pointing at a different element is a silent wrong answer — for an agent this is worse than an error, since it corrupts downstream reasoning (reading the wrong text, pressing the wrong control).Expectation (design discussion needed): the session could stamp each snapshot with a generation counter and remember which generation served the refs the client last saw; commands taking
@refargs could then either (a) warn in the response when the tree was replaced since that generation, or (b) compare the resolved node's label against the recordedrefLabelfor that ref and refuse/warn on mismatch (the replayfallbackLabelmachinery already does something similar). Zero-cost paths should stay zero-cost; even a response-levelrefsMayBeStale: truemarker after a tree-refresh would let agents re-snapshot instead of trusting garbage.