A weight edit cannot teach a skill. A small LoRA can.
We tried to make Qwen3.5-2B heal broken UI-test locators with a closed-form weight edit and no gradients. Then we trained a LoRA, and did a test of it on a real phone.
The task
A UI test finds a button with a locator. When the app changes, the locator can fail. Then the Optics self-heal step sends a screenshot and the rows of the screen to the model. The model must select the row that the broken locator identified.
TARGET ELEMENT: //*[@resource-id="com.app:id/skip_notification_button"]
CURRENT SCREEN:
e1 Text "Get updates?"
e2 Button "Allow" [tap]
e3 Button "Not now" [tap]
→ {"keyword": "press_element", "target": "e3"}
Text names repeat the text of the button (see_1_tab). Purpose names give only the purpose (skip_notification_button for "Not now").
The changes that increased the score
We used 1,314 tap steps from 96 Android and iOS apps. We used 4-fold cross-validation by app. Thus, no model saw the apps in its score set.
The chart shows purpose names, end to end. On text names, the same steps give 89.0% → 95.5% → 98.6% → 98.4%.
The weight edits
The idea came from weight-space programming (WSP), by Tensor-Slayer. In the WSP post, an agent wrote facts into Qwen 0.8B. It changed 5 of 488 tensors. The recipe post and the earlier paper give the method.
WSP finds the direction of a behavior in the activations. Then WSP writes that direction into a small number of matrices in closed form. We wanted to know if WSP can add a skill.
Our best recipe ran each step of the fit set with a prompt hint and without the hint. At the token where the model writes the row name, we calculated the mean difference of the activations. Then we added a rank-1 update (similar to ROME) to the attention output projection in 5 of 24 layers. Each edit changed a matrix by 0.6% to 1.7%.
The chart shows text names on all 1,368 steps, with 4-fold CV. On 377 new steps, the edit alone got 58% of the gain of the hint. The edit did not get more than the prompt.
| Other methods | Result |
|---|---|
| Edits at the answer readout (temperature, head gain) | The edits repaired 0 of 60 errors. |
| MLP edits | The edits broke as many answers as they repaired. |
| Edits at the action token | The results were worse than with no edit. |
| Edits to the full-attention layers | The edits broke many correct answers. |
| More rules in the prompt | Each new rule made the score lower. |
| The "thinking loops" edit on the 2B model | 2 of 6 repaired, against 0 of 6. The sample is too small for a conclusion. |
Facts and skills
A fact is one link, for example "the capital of Japan" → "Tokyo". The link is in a small number of MLP rows, thus a rank-1 edit can write it. A heal is a skill: the model must apply a rule to screens and names that it did not see before.
- The edit copies the hint. We made the edit from the difference between two runs of the same model. One run had the hint, and one run did not. The edit got 58% of the gain of the hint, and no more.
- A stronger edit does not help. When we made the edit stronger, it broke correct answers at the same rate as it repaired incorrect answers. The gain stopped at 15 to 21 of 377 new steps.
- A LoRA can add the skill. A LoRA on 0.7% of the weights added 7 points on purpose names, where no prompt helped.
Our test used one 2B model and one task. Thus, the test does not prove that no closed-form edit can teach a skill. The test shows that the recipe that writes facts did not write this skill.
Side effects
- A different task on the same model (write an XPath from the page outline, 80 steps): weight edit 47 → 52, LoRA 46 → 59.
- All the scores come from apps that were not in the fit set or in the LoRA data.
- We did not run general benchmarks. Use the LoRA model only for this task.
On a real phone
We served the model with vLLM and connected Optics to it. The test suite operates Google Clock on a Pixel 4A, with an assert after each press. We broke the locators in 7 ways. The suite let only the model heal them.
| Break set | Correct heals | Test cases passed |
|---|---|---|
| Wrong ID (4 sets) | 58/60 → 64/64 | 14/16 → 16/16 |
| Wrong name (3 sets) | 7/19 → 49/50 | 0/12 → 11/12 |
The pilot found four bugs in the self-heal code of Optics:
- The hint rows put a disabled button first.
- "Stop watch" did not match "Stopwatch".
- The prompt did not have the element name.
- After a correct tap, the loop continued to tap.
The repairs are in optics-framework#705 and optics-framework#708.
Limits
- We used one model size (2B). We did not do a test of the edits on larger models.
- The purpose names are synthetic. In 1,512 names from 185 public test repositories, approximately 69% repeat the visible text.
- The eval screens come from third-party apps, thus we cannot share them. The code that made and scored the screens is public.
- The pilot used one device, one app and 7 break sets, with 2 runs each.
Get the code
The repository has the code, the LoRA adapter (60 MB) and each script for these numbers: github.com/chinmayajha/qwen-self-heal.
Prior work
- Tensor-Slayer: the WSP post, the recipe post, the code and Exploring Direct Tensor Manipulation in Language Models (2025).
- Meng et al., Locating and Editing Factual Associations in GPT (ROME, 2022) and Mass-Editing Memory in a Transformer (MEMIT, 2023).
- Hu et al., LoRA: Low-Rank Adaptation of Large Language Models (2021).