Self-healing locator framework.
A multi-model Appium framework that re-identifies elements at runtime when their locators break — across DOM heuristics and vision models. Built at L&T Technology Services, deployed in production for two T-Mobile US MVNEs.
Locator maintenance is the boring tax on mobile automation.
Every UI change ships with a quiet bill: half a sprint of "fix the broken selectors." Multiply by two MVNE clients on two platforms (iOS + Android) on a weekly release cadence, and the team's #1 toil source is "what did findByXpath miss this week."
Existing tools were single-track: either pure DOM heuristics (brittle when the structure changes) or pure vision models (slow, expensive, drift-prone). Neither was good enough alone.
Cascade. Cheap heuristics first. Expensive models last.
Resolver tier 0 · the original locator
Always tried first. If it works, the spec proceeds. Free.
Resolver tier 1 · DOM heuristics
Walk neighbors of the cached locator. Try the same element by accessibility label, by class+text, by sibling-relative path. Sub-100ms. Catches ~70% of breakages.
Resolver tier 2 · text + visual hash
If tier 1 fails, run an OCR pass on the screen, find the element by its expected text, hash the visual region, cache for next run. ~500ms.
Resolver tier 3 · vision model
Last resort. Pass the screen + the original element's expected role to a multimodal model, ask "where is the X?" 2-3s.
Healing · update the cached locator
When any tier 1+ resolver succeeds, update the locator cache so the next run hits tier 0 again. Drift recovers itself.
Telemetry
Every healing event logs: which tier rescued it, how long it took, the new locator. Surfaces "this element is breaking weekly" patterns the team can fix at the source.
Centralized store · Flask + SQLite
The locator cache and healing telemetry live in a shared Flask service backed by SQLite. Every engineer and CI run heals against — and contributes to — the same cache, so a fix found on one machine lands for the whole pod instead of dying in isolated local state.
Watch the cascade heal — then watch it pay off.
Pick a scenario, run the spec, and watch the resolver fall through the tiers until something catches. Every heal writes back to the shared cache — so the next run on that target lands at tier 0 instantly. The suite gets faster as it runs.
What changed after this shipped.
Lessons
- Cascade beats monolith. The same instinct from CPU caches applies here.
- Always heal back to the cheap tier. If you update the cache after every tier 2+ win, the suite gets faster as it runs, not slower.
- Surface the recurring breakers. Healing makes the test suite robust, but the app still has unstable selectors. The telemetry let us fix the actual source.