[2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-config-object-types.md
This commit is contained in:
@@ -1485,6 +1485,10 @@ trigger, поле 5 = `0`/`8`.
|
||||
5 снапшотов `zont_config/*.{txt,yml}`. **Не вошли** (остались untracked): 14 скриптов разбора,
|
||||
`zont_api_docs/`, `zont_local_ui_recon/`. Не запушено — пуш отдельной командой.
|
||||
|
||||
> 🔴 **Коммит `1cc010a` содержит СЛОМАННУЮ версию** — правки после него (см. §6d) не закоммичены.
|
||||
> В `1cc010a` шаг 46 назван `action` и содержит PyYAML-анкоры `&idNNN`/`*idNNN` (69 шт).
|
||||
> Нужен `--amend` или новый коммит — **ждёт команды Alex**.
|
||||
|
||||
> ⚠️ Снапшот `18-43-24.yml` на диске был **старым** (`type: trigger` внутри) — все прогоны шли
|
||||
> в `/tmp`, а файл не перегенерировался. Перегенерирован на диск и закоммичен; бэкап старого —
|
||||
> `/tmp/backup_18-43-24.yml`. **Правило: гонять конвертер в целевой файл, а не в `/tmp`.**
|
||||
@@ -1709,18 +1713,154 @@ if not scenario.get('enabled', True):
|
||||
|
||||
---
|
||||
|
||||
## 6d. 🔴 Правки ПОСЛЕ коммита `1cc010a` — анкоры, `then`, потеря `if` (2026-09-17)
|
||||
|
||||
Три бага, найденных Alex'ом **глазами по файлу на диске** после коммита. Все исправлены,
|
||||
round-trip снова `diff = 0`. **Не закоммичено.**
|
||||
|
||||
### Баг 1: PyYAML-анкоры `&idNNN` / `*idNNN` (69 шт)
|
||||
|
||||
```yaml
|
||||
trigger: &id061 # ← анкор
|
||||
id: 9725
|
||||
...
|
||||
steps:
|
||||
- id: 9815
|
||||
if: *id061 # ← ссылка на ТОТ ЖЕ объект
|
||||
```
|
||||
|
||||
**Причина:** `scenario['trigger'] = flat_steps[0]['if']` — та же dict-**ссылка** в двух местах.
|
||||
PyYAML видит один объект в двух местах и пишет алиас вместо копии.
|
||||
|
||||
**Что пробовал (НЕВЕРНО):** `copy.deepcopy(...)` — убрало анкоры (69 → 3), но **оставило `if`
|
||||
в шаге**, то есть условие писалось дважды.
|
||||
|
||||
### Баг 2: `then` → `action` (Alex: «откуда там then блядь» → затем «Then конечно!!!»)
|
||||
|
||||
Я переименовал согласованное `action` в `then`, потом вернул `action`, потом снова `then`.
|
||||
**Итог — `then`.** Обоснование Alex: `then`/`else` — **пара** (поля 3/4 записи 46), поэтому
|
||||
ключи должны быть парными. Историческая форма `action: 9563` (одиночный id) — устарела.
|
||||
|
||||
### Баг 3: потеря `if` у ВСЕХ шагов
|
||||
|
||||
Показал Alex: `steps: - id: 8555 / action: - id: 8554` — **условие `8553` потеряно**.
|
||||
То же у `11827` (потеря `11823`) и у `9691` (`trigger:` пропал).
|
||||
|
||||
**Причина (одна на три симптома):** я убрал `if` из `dump_step`, а подъём `trigger:` ищет
|
||||
`'if' in flat_steps[0]`. Без `if` в шаге подъём **не срабатывал** — условие терялось везде:
|
||||
и у trigger (`9691`), и у manual (`11827`, `8555`).
|
||||
|
||||
**Фикс:** `if` возвращён в `dump_step`; для trigger-сценария вынимается **`pop`, а не копия**:
|
||||
|
||||
```python
|
||||
if (len(flat_steps) == 1 and isinstance(flat_steps[0], dict)
|
||||
and 'if' in flat_steps[0]):
|
||||
scenario['trigger'] = flat_steps[0].pop('if') # MOVED, не скопирован
|
||||
```
|
||||
|
||||
`pop` решает оба первых бага разом: нет второй ссылки → нет анкора; нет `if` в шаге → нет дубля.
|
||||
|
||||
### ✅ Форма после §6d (актуальная)
|
||||
|
||||
```yaml
|
||||
# trigger — условие наверху, шаг БЕЗ if
|
||||
- id: 9691
|
||||
name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
|
||||
enabled: true
|
||||
trigger:
|
||||
id: 9729
|
||||
object: 9495
|
||||
value: 1
|
||||
steps:
|
||||
- id: 9819
|
||||
then:
|
||||
- id: 9563
|
||||
descr: 'Включить выход 14/14: Прихожая (н/п)'
|
||||
target: 146064
|
||||
value: 1
|
||||
params: [0, 0, [], 0, 0, 0, 512]
|
||||
|
||||
# manual / if-конструкция — условие В шаге, then+else парой, вложенность рекурсивна
|
||||
- id: 8863
|
||||
if:
|
||||
id: 8853
|
||||
group: and
|
||||
children: [...]
|
||||
then:
|
||||
- id: 8862
|
||||
flag: 1
|
||||
if:
|
||||
id: 8858
|
||||
group: or
|
||||
children: [...]
|
||||
then:
|
||||
- id: 8859
|
||||
descr: puts "then-text"
|
||||
args: [0, 0, 0]
|
||||
- id: 8860
|
||||
unresolved: true
|
||||
else:
|
||||
- id: 8861
|
||||
descr: storeev A "alert"
|
||||
args: [0, 0, 0]
|
||||
|
||||
# manual многошаговый — сначала then целиком, потом следующий шаг
|
||||
- id: 11109
|
||||
steps:
|
||||
- id: 11827
|
||||
if: {id: 11823, object: 11190, value: 0}
|
||||
then:
|
||||
- id: 11191
|
||||
...
|
||||
- id: 11824
|
||||
wait: 20000
|
||||
- id: 11828
|
||||
wait: 0
|
||||
```
|
||||
|
||||
### Правки файлов (§6d)
|
||||
|
||||
| Файл | Место | Правка |
|
||||
|---|---|---|
|
||||
| `config-to-yml.py` | подъём `trigger` | `deepcopy` → **`pop('if')`** — условие ПЕРЕНОСИТСЯ, не копируется |
|
||||
| `config-to-yml.py` | `dump_step` type 46 | возвращён `node['if']`; `then` (не `action`) всегда **списком** |
|
||||
| `yml-to-config.py` | `emit_step` ×2, `emit_action` ×2 | читают `then`/`else`/`flag`; `46, step.get('flag', 0), ...` |
|
||||
|
||||
**Проверка:** `python3 test_roundtrip.py` → `✅ ROUND-TRIP ЧИСТЫЙ`, `661 → 661`, `686 → 686`;
|
||||
`diff` по отсортированным `#Z`-строкам = **0**; `action:` в сценариях = **0**, `then:` = 68,
|
||||
`if:` в шагах = 2 (только `11109` и `8863`), анкоров = **3** (законные: `[]`, `raw`, `sensors`).
|
||||
|
||||
### 🔴 Пятый круг (дополнение к §6c)
|
||||
|
||||
| Круг | Что затащил/сломал | Реплика Alex |
|
||||
|---|---|---|
|
||||
| 6 | `deepcopy` вместо `pop` → анкоры `&idNNN` | «че это за хуяня блядь?! нормально же блядь все было!» |
|
||||
| 7 | `then` → `action` | «откуда там then блядь» |
|
||||
| 8 | `if` убран из `dump_step` → потеря условия ВЕЗДЕ | «ты блядь теперь if-then конструкции запорол» |
|
||||
| 9 | `action` вместо `then` | «ДА БЛЯДЬ! КАК ТЫ БЛЯДЬ ДУМАЕШЬ?! Then конечно!!!» |
|
||||
|
||||
> 🔴 **Корневой урок:** симптом показывал **файл на диске**, а не `/tmp`. Я проверял `/tmp` и
|
||||
> докладывал «зелёно», Alex смотрел на диск и видел сломанное. **Перегенерировать целевой файл
|
||||
> сразу после правки, до доклада.** `grep` по диску — часть проверки, не опция.
|
||||
|
||||
---
|
||||
|
||||
## 6c. Факты о круглых итерациях (чтобы не повторять)
|
||||
|
||||
Эта сессия — **4 круга** вокруг одного и того же: я вырезал выдуманное поле, потом затаскивал его
|
||||
обратно под новым именем. Alex четыре раза указал на это прямым текстом.
|
||||
Эта сессия — **9 кругов** вокруг одного и того же: я вырезал выдуманное поле или ломал форму,
|
||||
потом возвращал/менял её под новым именем. Alex каждый раз указывал на это прямым текстом.
|
||||
|
||||
| Круг | Что затащил обратно | Как назвал это Alex |
|
||||
| Круг | Что затащил/сломал | Как назвал это Alex |
|
||||
|---|---|---|
|
||||
| 1 | `type` в сценарии | «я блядь тебе сказал какого хуя ты `type` вернул в сценарии» |
|
||||
| 2 | `field5` (якобы «вербатим, не служебный») | «какой нахуй field5 ЕБАТЬ ТЕБЯ В СРАКУ?! МЫ НАХУЯ ЕГО СНОСИЛИ!!!» |
|
||||
| 3 | `base5`/словарь `{manual:0,…}` | «КАКОГО ХУЯ ТЫ БЛЯДЬ КРУГАМИ ТО ХОДИШЬ?! НИ kind ни f5 ни field5 блядь ни type блядь быть не должно!!!» |
|
||||
| 4 | `trigger:` на уровне сценария + `action` вместо `then` | «и не if: а trigger:» → затем откат |
|
||||
| 5 | **`trigger` вырезал вообще, условие оставил в шаге** | «наличием поля trigger: блядь если ты еблан тупоголовый не можешь догадаться!» |
|
||||
| 4 | `trigger:` вырезан вообще + `action` вместо `then` | «и не if: а trigger:» → затем откат |
|
||||
| 5 | `trigger` вырезан вообще, условие оставлено в шаге | «наличием поля trigger: блядь если ты еблан тупоголовый не можешь догадаться!» |
|
||||
| 6 | `deepcopy` вместо `pop` → анкоры `&idNNN` | «че это за хуяня блядь?! нормально же блядь все было!» |
|
||||
| 7 | `then` переименован в `action` | «откуда там then блядь» |
|
||||
| 8 | `if` убран из `dump_step` → потеря условия ВЕЗДЕ | «ты блядь теперь if-then конструкции запорол» |
|
||||
| 9 | `action` вместо `then` (повторно) | «ДА БЛЯДЬ! КАК ТЫ БЛЯДЬ ДУМАЕШЬ?! Then конечно!!!» |
|
||||
|
||||
**Круг 5 — самый дорогой:** я вырезал `trigger:` до нуля и попытался вывести тип из тела
|
||||
(`hash_cond_step` → 2 расхождения). Правильный ответ всё это время называл Alex:
|
||||
@@ -1728,6 +1868,11 @@ if not scenario.get('enabled', True):
|
||||
(один шаг 46 → условие поднимается). Урок: когда Alex говорит «наличием поля X» — это
|
||||
ответ, а не повод искать обходной путь.
|
||||
|
||||
**Круги 6–9 — форма ломалась при каждом «улучшении»:** `then`/`else` — пара, менять на `action`
|
||||
нельзя; условие поднимается **переносом (`pop`)**, не копией (`deepcopy` даёт анкоры);
|
||||
`if` обязан жить в `dump_step`, иначе подъём `trigger:` не находит условие и теряет его у всех.
|
||||
Полный разбор — §6d.
|
||||
|
||||
**Урок:** «вырезал из вывода» ≠ «вырезал». Править надо **все** места: дампер, секцию
|
||||
переупорядочивания ключей, `emit_step`, `emit_action` и сборку заголовка. Полуправка выглядит
|
||||
как прогресс, но при следующем прогоне всплывает то же слово.
|
||||
@@ -1752,8 +1897,10 @@ if not scenario.get('enabled', True):
|
||||
4. **Старый конфиг `14-16-35`** — судьба не решена (с тестовыми сценариями логики).
|
||||
|
||||
⛔ **ЗАПРЕЩЕНО в YAML (выдумка парсера, Alex отвергает):** `type` (имя типа), `field5`, `kind`,
|
||||
`_f5`, `_kind`, `_else`, `run_scenario`, `trigger` на уровне сценария, `action` вместо `then`,
|
||||
подстановки имён/типов из чужих объектов. Полная таблица с обоснованием — §6a.
|
||||
`_f5`, `_kind`, `_else`, `run_scenario`, `action` вместо `then`, подстановки имён/типов из чужих
|
||||
объектов. Полная таблица с обоснованием — §6a.
|
||||
✅ **РАЗРЕШЕНО и обязательно:** `trigger:` на уровне сценария (выводится из тела — один шаг 46),
|
||||
`if` / `then` / `else` внутри шага 46 (это **поля записи 46**, пара `then`/`else`).
|
||||
Служебные `_`-ключи — только на нестандартных случаях (`_op`).
|
||||
|
||||
✅ **Тела действий — инлайн** внутри `then` (список; объект, если одно действие).
|
||||
|
||||
Reference in New Issue
Block a user