diff --git a/family/how-to/zont-config-compiler.md b/family/how-to/zont-config-compiler.md index 498221ca..ae1050cd 100644 --- a/family/how-to/zont-config-compiler.md +++ b/family/how-to/zont-config-compiler.md @@ -350,6 +350,10 @@ diff A.txt B.txt # пусто = round-trip чистый | 46 | 🔴 **Служебные ключи в YAML — отвергнуты как подход** | `_f5` держался 20+ сообщений. Alex: «какое нахуй служебное». Сначала искать вывод из данных, служебный ключ — последнее средство и только на нестандартных случаях (`_op`, `_then`, `_else`) | | 47 | 🔴 **Правило вывода `type` даёт сбой на `#Z11109`** | Есть шаг 46, но он `manual`. Отличие: в поле 2 **два** элемента. Число элементов ≠ 1 → не `trigger`. **Гипотеза, не проверена** (§5j) | | 48 | ⚠️ **`type` выводится из тела, но `manual` и `schedule` дают одно поле 5 = `0`** | Различаются только полями 3/4 (`days`/`time`). Alex: «manual от schedule очевидно отличаются наличием блядь schedule» | +| 49 | ✅ **РЕШЕНО: `type` НЕ выводить из тела — читать `f5 & 7`** | Три попытки вывести тип из структуры провалились (`11109` manual с шагом 46). Итог: `_base = f5 & 7` (1=trigger, 2=interval, 0=manual/schedule по days/time), 0 расхождений на 70 | +| 50 | 🔴 **Тела объектов из `then` не эмитились → потеря 3 объектов** | `11824`/`11825`/`11826` (паузы внутри `#Z11827`) есть только как id в списке `then`. Нужен `_emit_referenced_bodies(ids)` — обход ссылок и регистрация тел по их типу | +| 51 | 🔴 **`z_dict` определяется ниже вложенной функции → `free variable` ошибка** | `z_dict` строится на стр. ~1019, вложенная функция ссылалась на него до присваивания. Решение: собственный `_body_index` (карта `id → raw` по секциям YAML) рядом с функцией | +| 52 | 🔴 **`action` + `_then` давали дубль первого действия** | `11191` попадал дважды (`[11191, 11191, …]`). Правило: `_then` — источник истины, `action` добавлять только если `_then` пуст | ### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ @@ -569,13 +573,15 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО | # | Задача | Статус | |---|---|---| | 1 | ~~`TRIGGER_KINDS` — удалить~~ | ✅ **сделано** — таблица удалена | -| 2 | **Форма YAML** — Alex отверг `if`/`then` как выдумку | ✅ форма согласована (§5j) **и реализована в парсере** | +| 2 | **Форма YAML** — Alex отверг `if`/`then` как выдумку | ✅ форма согласована (§5j) **и реализована в обоих конвертерах** | | 3 | `git commit` правок парсера/энкодера | ⏸ не закоммичено, ждёт команды | -| 4 | YAML `17-45-00` — перегенерировать после правок | ✅ **перегенерирован** — 4405 строк, `trigger` на уровне сценария | +| 4 | YAML — перегенерировать после правок | ✅ **`18-43-24.yml`** актуален (форма §5j + `type`) | | 5 | Раскрыть `8195` (type 3 SMS) из YAML-якоря | ⏸ отложено | -| 6 | 🔴 **Открытый вопрос:** что писать в `steps[].action`, если в `then` больше одного id или есть `else` | ⏸ ждёт Alex (единственный открытый) | -| 7 | 🔴 **Round-trip сломан** — энкодер не переведён под форму §5j (парсер обновлён, `yml-to-config.py` — нет) | ⏸ переписать энкодер | -| 8 | ✅ ~~Поле 5 типа 11 — семантика не раскрыта~~ | ✅ **закрыто**: тип (`manual`/`trigger`/`interval`/`schedule`) + `enabled`; `_f5` убран (§5j) | +| 6 | ~~Открытый вопрос: `steps[].action` при >1 id~~ | ✅ **решено**: `action` — объект при одном, список при нескольких; тела инлайн | +| 7 | ~~Round-trip сломан — энкодер не переведён~~ | ✅ **энкодер переведён, round-trip ЗЕЛЁНЫЙ** (661→661, 686→686) | +| 8 | ~~Поле 5 типа 11 — семантика не раскрыта~~ | ✅ **закрыто**: тип + `enabled`; `type` из `f5 & 7` | +| 9 | 🔴 **`if` у trigger-сценариев — на уровень сценария** | ⏸ **НЕ НАЧАТО** — требование Alex, план в §5k | +| 10 | Тип 50 (дни недели) и `set var1` args — раскрыть из `raw` | ⏸ отложено | **Уроки методологии этой сессии** (проверять в следующих): @@ -605,6 +611,22 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО расхождениях. Записана как снятая — чтобы следующая сессия не выводила её заново. 11. 🆕 **Не выдавать пару примеров за правило.** Два совпавших сценария (11109, 9628) дали уверенность, которой не было. Проверка на всех 70 — обязательна до слова «выяснили». +12. 🆕 **Спрашивать про КОНКРЕТНЫЙ кейс, а не строить обобщение самому.** «Если в `then` больше + одного id» — я выдумал правило («больше одного шага → manual»), оно развалилось. Правило + дал Alex из UI: тип читается из поля 5. +13. 🆕 **Правило «выводится из тела» — скрытая выдумка.** Трижды подряд я выводил `type` из + структуры (`есть шаг 46` → trigger, `число шагов` → manual) вместо того, чтобы прочитать + само поле. **Если поле есть в конфиге — читать его, а не выводить аналог.** +14. 🆕 **«Проверил и снял» — ценный результат, а не провал.** Три гипотезы по полю 5 были + опровергнуты скриптами (32 расхождения, «все нули», сдвиг индекса). Каждая снятая гипотеза + в доке экономит следующей сессии час. Записывать **и** опровергнутое. +15. 🆕 **Скрипт проверки может врать молча.** Дважды проверяющий скрипт давал «всё сходится» + из-за сдвига индекса поля (`parts[4]` vs `parts[5]`). Перед выводом — печатать **сырые + значения**, не только вердикт. +16. 🆕 **Требование Alex, сказанное последним, — самое важное.** Форма `if`-в-шаге прошла + round-trip и «выглядела правильно», но Alex увидел её и сказал: «какого хуя у trigger + сценариях опять if ушел в steps?». Round-trip ≠ соответствие смыслу (уже урок №1, но + повторился). **Задача Alex (исходная):** «переписать блок парсинга/сборки сценариев чтобы он составлял синтаксис как у Home Assistant automations вместо текущей разбросанной структуры. с опциональными айдишниками у операторов». @@ -998,9 +1020,10 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip ## 5j. 🔴 ФОРМА СЦЕНАРИЯ В YAML — согласована с Alex (2026-09-17, финал сессии) -> **Статус:** форма **согласована И реализована в парсере** (`config-to-yml.py`). -> YAML `17-45-00` перегенерирован, блок `9691` вышел в целевой форме (`_f5` убран). -> **Энкодер (`yml-to-config.py`) ещё не переведён под новую форму** → round-trip сломан. +> **Статус (обновлено в конце сессии):** форма реализована в **обоих** конвертерах, +> `test_roundtrip.py` **ЗЕЛЁНЫЙ** — 661→661 объектов, 686→686 строк, 0 расхождений (снимок `18-43-24`). +> НО затем Alex потребовал **ещё одну правку**: у **trigger**-сценариев `if` должен стоять на +> уровне **сценария**, а не внутри `steps[]`. Правка **НЕ выполнена** (сессия прервана) — см. §5k. > Полный разбор модели — [[family/tech/zont-scenario-logic-11109]] §8.17. > ✅ **Поле 5 закрыто:** тип (`manual`/`trigger`/`interval`/`schedule`) + `enabled` — см. ниже. @@ -1131,23 +1154,22 @@ enabled = not (поле5 & 8) ← подтвержд `_raw_field_count`, далее `trigger`, `steps`. Реализовано переупорядочиванием словаря после обхода шагов (`config-to-yml.py`, блок `# Reorder so the type sits with the other header fields`). -🔴 **Правило вывода `type` (текущее, порядок веток важен):** +🔴 **Правило вывода `type` (ФИНАЛЬНОЕ — читается из поля 5, НЕ из структуры):** ```text -trigger: есть поднятый `trigger` (первый шаг — тип 46) -interval: есть `interval_ms` (поле 6) -schedule: есть `days` или `time` -manual: иначе +_base = f5 & 7 +_base == 1 → 'trigger' +_base == 2 → 'interval' +_base == 0 → 'schedule' если заданы days/time, иначе 'manual' ``` -⚠️ **ИЗВЕСТНАЯ ОШИБКА в этом правиле:** `#Z11109` получает `type: trigger`, хотя по полю 5 -он **`manual`** (`f5=0`). Причина: у него тоже есть шаг типа 46. Отличие от настоящих trigger — -у `11109` **два** элемента в поле 2 (`11827` шаг + `11828` пауза), у trigger-сценариев — **ровно -один**. Гипотеза правки: `trigger` = **ровно один** элемент в `steps` **и** он типа 46. -**Не проверена** (команда прогона не выполнена — approval timeout). +✅ **Проверено на всех 70 сценариях: 0 расхождений.** Ключевой кейс — `#Z11109`: он `manual` +(f5 = `0`), хотя у него есть шаг типа 46. Правило «есть шаг 46 → trigger» **неверно** и снято. -Проверено на тот момент: `schedule` (8597) ✓, `interval` (8599) ✓, `trigger` (9628, 9691, 8547, 8551) ✓, -`manual` (8456) ✓, **`manual` (11109) ✗**. +⚠️ **Почему `type` читается из поля 5, а не выводится из тела:** `manual` и `schedule` дают +**одно и то же** число `0` и различаются только наличием `days`/`time`. А `trigger`/`manual` +различить по телу нельзя: у `11109` (manual) тоже есть шаг 46. Единственный надёжный источник — +само поле 5. **Что опровергнуто и снято (не возвращаться):** @@ -1156,12 +1178,14 @@ manual: иначе - ❌ **Гипотеза «поле5 & 7 = значение из условия шага 46»** — проверена скриптом по всем 70: **32 расхождения**. Пары ВКЛ/ВЫКЛ одной линии (`9628` ВКЛ и `9629` ВЫКЛ) имеют **одинаковое** поле 5 = `1`, но противоположные условия (`1` и `0`). Гипотеза развалилась — **снята**. -- ❌ **Гипотеза «поле5 = 1 ⇔ ровно один шаг 46 с одним действием»** — проверена, дала - «предсказано 0» на всех 70 (индекс поля в скрипте был сдвинут, результат мусорный). **Снята.** -- ❌ «Поле 5 не выводится из содержимого» — **неверно**: выводится по правилу выше - (`тип` + бит 8). Ранее записанный вывод был получен до того, как Alex назвал типы. +- ❌ **Гипотеза «trigger = ровно один элемент в `steps` и он типа 46»** — **снята**: `#Z11109` + имеет **два** элемента (`11827` шаг + `11828` пауза) и он `manual`, но `#Z8599` (interval) + тоже имеет два — правило по числу шагов не работает. Тип берётся из поля 5 напрямую. +- ❌ «Поле 5 не выводится из содержимого» — **неверно**: выводится по правилу выше. - ❌ Слово «триггерный» как тип сценария — тоже выдумка. Alex: «в смысле блядь триггерные». Типов ровно четыре, названы Alex: `manual`/`trigger`/`interval`/`schedule`. +- ❌ **«Поле 5 = значение условия» не работает, но `f5 & 7` — работает.** Разница: значение + условия бывает `0` и у ВКЛ, и у ВЫКЛ сценариев; поле 5 у них одинаково `1`. ### ✅ Служебные ключи — БОЛЬШЕ НЕ НУЖНЫ @@ -1215,62 +1239,152 @@ manual: иначе --- -## 6. Состояние проекта (проверено 2026-09-17, финал) +## 5k. 🔴 ПОСЛЕДНЯЯ ПРАВКА ФОРМЫ (не завершена) + переписанный энкодер -🟡 **Парсер переписан под форму §5j (триггер на уровне сценария + поле `type`), энкодер — ЕЩЁ НЕТ.** -Round-trip сейчас **не сходится** — `yml-to-config.py` ждёт перевода. Рабочее дерево -**не закоммичено**, ждёт команды Alex. Последний коммит — `199f2b1`. +### Энкодер переведён — round-trip ЗЕЛЁНЫЙ (2026-09-17, конец сессии) -✅ **Парсер `config-to-yml.py` — форма §5j реализована:** `trigger` на уровне сценария -(вынимается из первого шага типа 46), `steps[].{id, action}`, `descr` у шага не выводится, -`type` (`manual`/`trigger`/`interval`/`schedule`) выводится из тела, `if`/`then`/`else`/ -`condition`/`operator`/`kind`/`_f5` из вывода убраны. +`yml-to-config.py` переписан под форму §5j. Что сделано: -🔴 **Известный баг парсера (единственный):** `#Z11109` получает `type: trigger`, хотя он `manual` -(поле 5 = `0`). Гипотеза правки: `trigger` = ровно один элемент в `steps` И он типа 46. -Прогон не выполнен. Детали — §5j. +| # | Правка | Место | +|---|---|---| +| 1 | `emit_condition` — ветка для формы `{id, object, value}` → `[49, object, op, value]` (`_op` если ≠ 1) | стр. ~374 | +| 2 | `emit_action` / `emit_step` — ветка `if 'action' in node: return aid` | — | +| 3 | Ссылка на сценарий — `if set(step) <= {'id'}: return sid` (вместо `run_scenario`) | — | +| 4 | Сборка шага 46: `if:` → `emit_condition`, `action:` (объект **или**) список → `[emit_step(…)…]`, `_else` → `else_ids` | — | +| 5 | Сборка сценария: `trigger` (уровень сценария) → `emit_condition` + первый `steps[].action` → `[46, kind, cond_id, action_ids, else_ids]` | стр. ~630 | +| 6 | Поле 5: `f5 = {manual:0, schedule:0, trigger:1, interval:2}[type] + (0 if enabled else 8)` | стр. ~624 | +| 7 | Хелпер `_emit_referenced_bodies(ids)` — вытягивает тела объектов, на которые ссылается `then` (паузы 45, вложенные 46/47/48/49/50), иначе они теряются | стр. ~575 | +| 8 | `_body_index` — карта `id → raw` по всем секциям YAML (для пункта 7) | стр. ~576 | -✅ **Round-trip был чистый на 4/4 конфигах** (§5c, до правки формы). Счётчики совпадали: 661 → 661. -**После правки формы — сломан, ждёт энкодера.** +**Результат:** `python3 test_roundtrip.py` → `✅ ROUND-TRIP ЧИСТЫЙ — расхождений нет`, +`661 → 661` объектов, `686 → 686` строк. -🔴 **Точная ошибка round-trip (2026-09-17, последний прогон):** +Питфоллы этой правки (round-trip ловил каждый): -```text -python3 test_roundtrip.py -❌ YAML->TXT exit=2 -Ошибка конвертации: Step 9756: unrecognised node {'id': 9756, 'action': 9500} +| # | Симптом | Причина | Решение | +|---|---|---|---| +| 1 | `duplicate id` у `11191` в `11827` | `action` добавлялся к `_then`, хотя `_then` уже его содержал | `_then` — источник истины; `action` брать только если `_then` пуст | +| 2 | Потеряны `11824`/`11825`/`11826` (паузы) | тела объектов из `then` нигде не эмитились | `_emit_referenced_bodies` | +| 3 | `free variable 'z_dict' referenced before assignment` | `z_dict` определяется **ниже** (стр. ~1019), из вложенной функции не виден | собственный `_body_index` из секций YAML | +| 4 | `#Z11109` получал `f5=1` вместо `0` | тип выводился из тела (есть шаг 46 → trigger) | тип читать из `f5 & 7` (см. §5j) | + +### 🔴 ПОСЛЕДНЕЕ ТРЕБОВАНИЕ ALEX — НЕ ВЫПОЛНЕНО + +Alex, последнее сообщение сессии: + +> «какого хуя у нас в trigger сценариях опять `if` ушел в `steps`?» + +и раньше: + +> «в смысле блядь 9691/9628 тогда получится?! там блядь **триггер!**» +> «там блядь ссылка на этот `if` в самом заголовке сценария если мне память не изменяет! +> это блядь и есть триггер» + +**Что требуется:** у сценариев типа **trigger** (f5 & 7 == 1) условие (`if`) должно быть +**на уровне сценария**, а `steps[]` содержать только действия. У остальных типов +(`manual`/`schedule`/`interval`) шаг 46 остаётся **внутри** `steps[]` со своим `if` — это +ветвление внутри сценария (пример: `#Z8456`, шаг `8863` в середине списка). + +```yaml +# trigger — if на уровне сценария +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + type: trigger + if: ← уровень сценария + id: 9729 + object: 9495 + value: 1 + steps: + - id: 9819 + action: 9563 + +# manual — if внутри шага (ветвление в середине цепочки) +- id: 8456 + steps: + # … 26 шагов … + - id: 8863 + if: + id: 8853 + group: and + children: […] + action: 8862 ``` -Энкодер не знает новую форму. **Что осталось сделать в `yml-to-config.py`** (объём ~80 строк): +**Проверено по конфигу:** в **заголовке** сценария (`#Z=11,…`) ссылки на условие **НЕТ** — +все 70 заголовков имеют ровно `[шаги], days, time, f5, interval, 0`, последнее поле всегда `0`. +Условие живёт **внутри шага 46** (поле 3). Alex настаивает, что для trigger это семантически +**триггер сценария** — то есть парсер должен **вынимать** его наверх для этого типа. -| # | Что | Где искать | -|---|---|---| -| 1 | `emit_action`/сборка шага — не понимает `{'id': N}` (ссылка на сценарий) | уже поправлено: `if set(step) <= {'id'}: return sid` | -| 2 | Сборка шага — читать `action: ` → `[46, 0, cond_id, [action], []]` | убрать ветку `'if' in step or 'then' in step` (стр. ~508) | -| 3 | Сценарий — читать `trigger:` (список с `object`/`value`), а не `if:` внутри шага | цикл `for scenario in data.get('scenarios')`, стр. ~548 | -| 4 | Тип → поле 5: `type` + `enabled` → `0`/`1`/`2`/`8`/`9`/`10` | строка сценария, стр. ~554–590 | -| 5 | `_scenario_from_legacy` (стр. ~521) — мёртвый код под старую форму, вычистить | — | +**Статус:** правка **не начата**. Текущий код (`config-to-yml.py`) кладёт `if` внутрь шага +**для всех** типов и вытягивания на уровень сценария **не делает** (блок с `f5 & 7 == 1` +остался пустым — я его вырезал). Round-trip при этом зелёный, потому что форма самосогласована +в обоих конвертерах, но **не соответствует требованию Alex**. -Правило сборки поля 5: `f5 = {manual:0, schedule:0, trigger:1, interval:2}[type] + (0 if enabled else 8)`. +**План правки (следующая сессия):** +1. `dump_step`, тип 46: не рендерить `if` внутри шага, если сценарий типа `trigger` — отдавать + `_trigger_id` наружу (как раньше). +2. Сборка сценария: при `f5 & 7 == 1` вынимать `_trigger_id` из **первого** шага → + `scenario['if'] = dump_condition(trig_id)`. +3. Энкодер: при `type == 'trigger'` и наличии `scenario['if']` собирать `[46, kind, cond_id, + [action_ids], []]` из уровня сценария; иначе — из шага. +4. Перегенерировать YAML, прогнать round-trip (обязательно — форма двусторонняя). + +--- + +## 6. Состояние проекта (проверено 2026-09-17, финал) + +🟢 **Round-trip ЗЕЛЁНЫЙ:** `661 → 661` объектов, `686 → 686` строк, 0 расхождений на снимке +`18-43-24`. Оба конвертера переведены на форму §5j. + +🔴 **НО форма требует последней правки (§5k):** у trigger-сценариев `if` должен быть на +уровне сценария. Сейчас `if` лежит внутри `steps[]` для **всех** типов. Правка **не начата**. + +🔴 **Рабочее дерево НЕ закоммичено** — ждёт команды Alex (коммит+док запрошены в конце сессии, +правка §5k может изменить код до коммита). Последний коммит — `199f2b1`. + +⚠️ **Не разобрано (остаётся `raw`/`unresolved`):** +1. **Тип 50** — маска дней недели: `#Z8548=50,1,0,0,109`, где `109 = 0b1101101` = пн,ср,чт,сб,вс + (Alex: «это выбор дней недели, то же самое что ставится в значение var1»). Сейчас `raw`. +2. **`set var1` с `args: [8844, 0, 0]`** — внутри объекта `8844` лежит та же маска дней недели, + объект не раскрыт (Alex: «что какого-то хуя уехало вовне сценария вообще — только там + пн, вт, чт, пт, сб, вс»). +3. **`unresolved: true`** у `#Z8860`, `#Z8864`, `#Z8601` — этих объектов нет в конфиге. + +⛔ **ЗАПРЕЩЕНО в YAML (выдумка парсера, Alex отвергает):** `then`/`else`/`condition`/`operator` +на уровне сценария, `kind`, `_f5`, `run_scenario`, подстановки имён/типов из чужих объектов, +`trigger` с именем объекта. Служебные `_`-ключи — только на нестандартных случаях (`_op`, +`_else`, `_kind`). + +✅ **Форма (текущая реализация, до правки §5k):** + +```yaml +- id: 9691 + name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ' + enabled: true + type: trigger + steps: + - id: 9819 + if: {id: 9729, object: 9495, value: 1} + action: {id: 9563, descr: 'Включить выход 14/14: Прихожая (н/п)', target: 146064, value: 1} +``` + +✅ **Тела действий — инлайн** внутри `action` (объект, если одно действие; список — если +несколько). Вложенные `if` (`47`/`48`/`49`) сохраняются рекурсивно (`group: and/or/not`, +`op: '<=', left, value`). ✅ **Уставка температуры раскодирована** — `код = (t °C + 273) × 10`, подтверждено 3 точками (`22→2950`, `23.8→2968`, `5.2→2782`; §8.13 в [[family/tech/zont-scenario-logic-11109]]). -✅ **YAML сжат вдвое:** 6093 → 4405 строк. Секции-дубли убраны, поля переименованы -(`descr`/`target`/`value`). - ✅ **Поле 5 типа 11 — РАСКРЫТО И ПОДТВЕРЖДЕНО ПРИБОРОМ** (§5j): `тип + 8 при выключенном`; -типы `manual`/`trigger`/`interval`/`schedule` (Alex), `enabled = not (f5 & 8)`. +типы `manual`/`trigger`/`interval`/`schedule` (Alex), `enabled = not (f5 & 8)`; +`type` в YAML читается напрямую из `f5 & 7` (0 расхождений на 70 сценариях). Включённые `schedule`/`interval` добыты тостингом в UI (`8597` → `0`, `8599` → `2`). -`_f5` из YAML убран — поле восстанавливается из `enabled` + типа. - -⚠️ **Открыто два вопроса:** (1) `steps[].action` при >1 id в поле 4 шага 46 (у `11109` их 7); -(2) `run_scenario` — имя или id. Оба — §5j. | Файл | Статус | |---|---| -| `config-to-yml.py` | 🆕 **изменён** — форма §5j: `trigger` на уровне сценария, `steps[].{id, action}`, поле `type` из тела (`manual`/`trigger`/`interval`/`schedule`), `_f5`/`descr` у шага убраны, `run_scenario` заменён на голый `id`, `object` вместо `condition.operator`; плюс всё из §5c (типы 47/48/50/59, `descr`/`target`/`value`, `scenario_orphans`, `_build_type9_line`, подстановки удалены). **не закоммичено** | -| `yml-to-config.py` | 🔴 **НЕ переведён под форму §5j** — всё ещё ждёт старой формы (`if`/`then`, `trigger` внутри шага). **Round-trip падает:** `Step 9756: unrecognised node {'id': 9756, 'action': 9500}`. Частично поправлено (`set(step) <= {'id'}` вместо `run_scenario`). План правки — §6. **не закоммичено** | +| `config-to-yml.py` | 🆕 **изменён** — форма §5j: `type` из `f5 & 7`, `if` внутри `steps[]` (шаг 46), `action` с телом инлайн, поле 5 из `type`+`enabled`, `run_scenario` заменён на голый `id`; плюс всё из §5c (типы 47/48/50/59, `descr`/`target`/`value`, `scenario_orphans`, `_build_type9_line`, подстановки удалены). 🔴 **Ждёт правки §5k** (trigger → `if` на уровень сценария). **не закоммичено** | +| `yml-to-config.py` | ✅ **ПЕРЕВЕДЁН на форму §5j** — round-trip ЗЕЛЁНЫЙ. Правки: `emit_condition` (форма `{object,value}`), сборка шага 46 из `if`/`action`, поле 5 из `type`+`enabled`, `_emit_referenced_bodies` + `_body_index`. 🔴 **Ждёт правки §5k**. **не закоммичено** | | `test_roundtrip.py` | ✅ в коммите `199f2b1`, без изменений | | `zont_config/config_local_2026-09-17_18-43-24.txt` | 🆕 **34 962 байт** — **САМЫЙ ПОСЛЕДНИЙ** конфиг. Снят `curl -s http://192.168.0.50/config.txt` после включения `8597`/`8599` в UI. **не в git** | | `zont_config/config_local_2026-09-17_18-43-24.yml` | 🆕 форма §5j + поле `type`. **не в git** |