I let Claude create an automation. It creates multiple variables, where variable A might reference variable B. The tool gets the correct order (B before A) but writes the variables alphabetically, which creates problem because now A references B which is undefined at that point.
## 📋 Bug Description
`ha_config_set_automation` with the full-config path (`config=`) reorders dict keys alphabetically before persisting. Inside a `variables:` block this silently breaks variables that reference earlier variables in the same block.
## 🔄 Steps to Reproduce
1. Call `ha_config_set_automation` (create, no `identifier`) with a `variables:` block whose keys are deliberately NOT in alphabetical order:
{"actions": [{"variables": {"zzz_erste": "1", "yyy_zweite": "2",
"aaa_dritte": "3", "mmm_vierte": "4"}}],
"alias": "ZZZ TEMP order test",
"mode": "single",
"triggers": [{"event_type": "never_fires", "trigger": "event"}]}
2. Call `ha_config_get_automation` on the created automation.
3. Observe the key order in the returned `actions[0].variables`.
## ✅ Expected vs ❌ Actual Behavior
**Expected:**
Key order preserved as submitted: `zzz_erste, yyy_zweite, aaa_dritte, mmm_vierte`. HA renders a `variables:` mapping in insertion order and explicitly supports a later variable referencing an earlier one, so the order is semantically meaningful, not cosmetic.
**Actual:**
Returned sorted alphabetically: `aaa_dritte, mmm_vierte, yyy_zweite, zzz_erste`.
Scope, as narrowed down by testing:
- `config=` (full replacement) → **sorts**
- `python_transform=` → preserves order (verified: a 5-key block submitted as `regen_erwartet, offene_tueren, offene_eg, offene_keller, offene_og` came back unchanged)
- `ha_config_get_automation` read path → preserves order (an automation written in an earlier session still reads back unsorted)
So the sorting happens on the full-config write path only.
**Why this matters — the failure is silent.** In a real automation I had:
variables:
stunden: "{{ ...weather forecast slice... }}"
regen_erwartet: "{{ 'ja' if (stunden | selectattr(...) ...) else '' }}"
offene_tueren: "{{ expand('binary_sensor.group_1') | ... }}"
meldung: "{% if offene_tueren %}...{% endif %}
{% if regen_erwartet and offene_og %}...{% endif %}"
After the write, `meldung` was sorted to the **front** of the block, so it rendered against undefined `offene_tueren` / `regen_erwartet`. No error, no log entry — the automation loads and runs, the notification just always takes the empty branch. This would have shipped as a wrong-but-plausible notification if I hadn't diffed the config after writing.
## 💬 Triggering Prompt & Tool Call
**User prompt:** (paraphrased — original was a feature request in German, no relevant detail lost)
"Extend the existing mode with two more stages ... please look at all the automations and think through how best to combine them."
**Tool call(s):**
ha_config_set_automation(
identifier="automation.example_start",
config_hash="<hash>",
config={... "actions": [... {"choose": [... {"sequence": [
{"action": "weather.get_forecasts", "response_variable": "wetter", ...},
{"variables": {
"stunden": "{% ... %}{{ (wetter[...]['forecast'] or [])[:h] }}",
"regen_erwartet": "{{ 'ja' if (stunden | selectattr(...)) else '' }}",
"offene_tueren": "{{ expand('binary_sensor.group_1') | ... }}",
"offene_eg": "{{ expand('binary_sensor.group_2') | ... }}",
"meldung": "{% if offene_tueren %}...{% endif %}..."
}},
{"action": "notify.send_message", "data": {"message": "{{ meldung }}"}, ...}
]} ...]} ...]}
)
→ success
ha_config_get_automation("automation.example_start")
→ variables returned as:
meldung, offene_eg, offene_keller, offene_og, offene_tueren, regen_erwartet, stunden
(alphabetical — meldung now precedes everything it references)
## 🔧 Environment
- **ha-mcp Version:** 7.14.1 (note: 7.14.2 was available at time of report; not yet retested on it)
- **Custom Component:** not detected
- **Installation Method:** addon
- **MCP Transport:** http
- **MCP Client:** unknown (client did not advertise itself)
- **AI Model:** Anthropic Claude — Opus 5
- **Operating System:** Linux 6.18.34-haos-raspi (aarch64)
- **Python Version:** 3.13.12
- **Home Assistant Version:** 2026.7.1
- **Connection Status:** Connected
- **Entity Count:** 1065
## ⚙️ ha-mcp Configuration
- **enable_beta_features:** `False`
- **enable_websocket:** `True`
- **enable_dashboard_partial_tools:** `True`
- **enable_tool_search:** `False`
- **tool_search_max_results:** `5`
- **enable_yaml_config_editing:** `False`
- **enable_filesystem_tools:** `False`
- **enable_code_mode:** `False`
- **enabled_tool_modules:** `all`
- **disabled_tools_count:** `0`
- **pinned_tools_count:** `0`
## 🚨 Error Messages
None — all tool calls returned success. That is the core of the report:
the corruption is silent on both the write and the subsequent read.
## 💡 Additional Context
**Suggested fixes, roughly in order of preference:**
1. Preserve insertion order on the full-config write path — most likely a `sort_keys=True` on a `json.dumps`, or a `sorted()` in a dict-normalisation/hashing helper leaking into the persisted payload. `python_transform` already does the right thing, so the two paths could probably share that serialisation.
2. If sorting is deliberate (e.g. to make `config_hash` order-independent), then sort only for the *hash* computation and persist the original ordering.
3. Failing both: emit a `best_practice_warnings` entry when a `variables:` block contains a key whose template references another key in the same block. That checker already exists for template-vs-native conditions and would have caught this.
**Same class of risk elsewhere:** any tool taking a full config dict where key order carries meaning — worth checking `ha_config_set_script` and `ha_config_set_dashboard` for the same behaviour.
**Workaround for other users:** split interdependent variables into separate consecutive `variables:` steps. Ordering *between* actions is preserved; ordering *within* a dict is not.
💬 What Happened?
I let Claude create an automation. It creates multiple variables, where variable A might reference variable B. The tool gets the correct order (B before A) but writes the variables alphabetically, which creates problem because now A references B which is undefined at that point.
BTW: Thank you for your awesome work!
📋 Copy-Paste from Conversation