CSS selectors and test IDs are brittle. A component rename, a library upgrade, or a design refresh can changeDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Meza-dev/Ghostly/llms.txt
Use this file to discover all available pages before exploring further.
data-testid="submit-btn" to data-testid="login-submit" overnight, turning a perfectly healthy test suite into a wall of red. Traditional test frameworks require a developer to diagnose the failure, hunt down the new selector, and push a fix — even when the underlying application behavior hasn’t changed at all.
Ghostly’s self-healing system addresses this at runtime. When a step fails because its selector can’t be found on the page, the healer agent captures the current state of the DOM, asks the LLM to identify an alternative selector for the intended element, and retries the step — all without stopping the run or requiring any human input.
Self-healing only activates in assisted mode (v2). Runs using
runFlow directly (scripted steps without assist.v2: true) fail immediately on selector errors and do not attempt recovery.The Problem in Detail
Consider a login form. Your test step reads:[data-testid="submit-login"]. Playwright throws a timeout error because the original selector is nowhere on the page. Without self-healing, the run fails and a developer has to intervene.
How the Healer Works
When a step fails inside the assisted v2 loop, the following sequence fires up tomaxHealingAttemptsPerStep times (0–3):
Detect Failure
The pipeline catches the step error and emits a
step_failure event. If maxHealingAttemptsPerStep is greater than zero, healing begins immediately.Capture Current Accessibility Tree
The Observer calls
captureObserverSnapshot(), which uses Playwright’s ariaSnapshot({ mode: "ai" }) on the live page. This produces a structured markdown of every visible interactive element — buttons, links, textboxes, headings — without any CSS selector dependencies.Ask the LLM for an Alternative
The Healer function (
HealerFn) receives a HealerContext containing the goal, the failed step, the error message, the current snapshot, the execution history, and any codeHints from the Scanner package. The LLM is asked to propose replacement steps that accomplish the same intent.Sanitize and Validate
sanitizeHealerSteps() filters out ambiguous selectors (bare button, input, etc.), rejects [ref=eN] accessibility reference tokens that aren’t valid CSS, and caps the response at 3 steps. Invalid proposals are discarded silently.Apply the Healed Steps
Each sanitized replacement step is executed in order. A
heal_action event is emitted for each. If the healer’s steps succeed and the original step is no longer needed (because the healer replaced it), it is marked dropped in the plan progress.Healing Events
The entire healing lifecycle is observable through the event stream:| Event | Payload highlights |
|---|---|
heal_start | attempt, maxAttempts, nodeCount (snapshot size) |
heal_action | step (the replacement), rationale (LLM explanation) |
heal_success | step, replacedByHealer, skippedOriginal |
heal_failure | error or reason: "no-valid-steps" |
CodeHints: Source-Code Context for the Healer
The Ghostly Scanner package performs static AST analysis of your application’s source code to extract component metadata. This metadata is passed to the healer ascodeHints, giving the LLM accurate knowledge of what test IDs and aria labels actually exist in the codebase rather than guessing from the DOM snapshot alone.
The codeHints object is validated by codeHintsSchema in apps/api/src/routes/run.ts:
Example CodeHints Payload
Here is what a typicalcodeHints payload looks like when the Scanner processes a React login form component:
Selector Sanity Rules
The healer applies strict validation before accepting any LLM-proposed selector. Proposals are rejected if they:- Are bare tag names:
button,input,textarea,select,a - Are overly generic type selectors:
button[type="submit"],input[type="text"],input[type="password"],input[type="email"],[type=submit],form button,form input - Contain accessibility ref tokens:
[ref=e42](internal to Playwright’s aria engine, not valid CSS)