This is a personal proof-of-concept project. It is not intended for production use. Please refrain from using it.
Skip to content

Self-heal & root-cause

Upliftr is AI debugging agents that debug your app: they drive your flow (the browser) and read your servers (read-only) to root-cause failures, never mutating your code, data, or infrastructure. Two behaviors set the agents apart inside a run: they self-heal through UI change, and they root-cause genuine failures, down to the backend error, into deduped issues. This page explains both at the engine level.

Self-healing

A conventional test pins itself to a locator, button.btn-primary[data-id="create"], and dies the moment that locator goes stale. The agents carry no stored locators. Each step describes intent, and the agent re-reads the live DOM on every run.

How it re-grounds

intent: "Click 'Create account'"

   ├─ read_dom ──► title, url, visible text, numbered interactive elements (#ref)

   └─ act ──────► click the element whose live label/role/text matches the intent

The model resolves the target fresh, at that instant, from the current page. For a scoped assertion (visible_text with a selector_hint, or element_visible) the element is likewise resolved live. So the kinds of changes that wreck a traditional suite don't register:

UI changeConventional suiteUpliftr
Button class renamed🔴 breaks🟢 heals
Element moved in the DOM🔴 breaks🟢 heals
Label text tweaked🔴 breaks🟢 heals (same intent)
Behavior actually regressed🔴 fails🔴 fails, correctly

The goal: only fail when the behavior is wrong, never when the markup merely moved.

Root-cause capture: from UI symptom to backend cause

When an assertion genuinely fails, the engine doesn't stop at "step 3 red." It already captured every failed network response during the case, including the traceparent header, from which it extracts a trace_id. The chain is:

1. assertion fails  ──►  e.g. "order summary shows USD, not EUR"
2. driver.root_cause()  ──►  the failing request + its trace_id
3. trace_id + a configured log backend  ──►  fetch matching server log lines
4. Claude synthesizes  ──►  { category, server_error, suspected_cause, suggested_fix, confidence, evidence }
5. attach to the report + the filed issue

If no trace_id is present, or no log backend is configured, you still get the in-browser root cause (the failing request and a summary), the server-side enrichment is best-effort and additive. See Connecting server logs for the backends (Loki, Datadog, Elasticsearch, a custom HTTP endpoint, or a file) and how to wire one up.

Why this matters

A German checkout that silently shows USD instead of EUR returns a healthy HTTP 200, nothing throws in the browser. An absent_text: ["USD"] check fails the run, the failing POST /api/checkout?cc=DE carries a trace_id, and the log backend resolves it to the real 500 GeoLookupError line on the server. Claude turns that into "backend error → cause → suggested fix." That's the seam no UI-only checker can reach.

From failure to deduped issue

reports.py builds a provider-agnostic issue from the case (including the server diagnosis), keyed by a stable fingerprint:

tw:<suite_id>:<case_id>:<assertion_id>:<failure_class>
  • First occurrence → a new issue is opened (built-in tracker, GitHub, GitLab, Jira).
  • Re-occurrence → Upliftr searches the tracker for the fingerprint and comments on the existing issue ("↻ Reproduced again") instead of opening a duplicate.

The issue body carries: what failed (expected vs. observed), what worked, the root cause (failing request + trace_id), the server diagnosis (error, suspected cause, suggested fix, log evidence), severity (driven by p0/smoke tags), the run URL, and the commit.

So a flaky environment or a long-standing regression produces one living issue that accrues occurrences, not a pile of duplicates every nightly run.

Putting it together

A run against staging will:

  1. Self-heal through cosmetic and structural UI churn.
  2. Fail precisely when behavior regresses, capturing the failing request and trace_id.
  3. Root-cause it to the backend when logs are connected.
  4. File one deduped, root-caused issue to your tracker, and comment on it on subsequent runs.

Configure destinations in Filing issues and log backends in Connecting server logs. For the full engine picture, see Architecture.

Upliftr · AI-native, self-healing E2E testing. Terms · Privacy · Legal Notice