diff --git a/personal/projects/zont-config-compiler.md b/personal/projects/zont-config-compiler.md index 36c6845c..7108c203 100644 --- a/personal/projects/zont-config-compiler.md +++ b/personal/projects/zont-config-compiler.md @@ -3,7 +3,7 @@ title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml namespace: personal type: how-to created: '2026-09-17' -updated: '2026-09-17k' +updated: '2026-09-17m' tags: - personal - zont @@ -1837,6 +1837,13 @@ rm -rf zont_config/_wip | 44 | 🔴 **Коммит после первого зелёного круга, а не «когда Алекс скажет»** | Правило «коммит только по команде» не отменяет **промежуточных** коммитов ради сохранности. Формулировка для будущего: зелёный круг → коммит WIP-ветки, команда нужна для `push` | | 45 | 🔴 **Не спрашивать «что делать» после того, как сам сломал, если план уже ясен** | Alex: «ЧТО ТЫ БЛЯДЬ ОПЯТЬ ОТ МЕНЯ ХОЧЕШЬ ЗАЕБАЛ». У него ответа нет — восстановление на ассистенте | | 46 | 🔴 **При поиске потерянного — сначала `git fsck --lost-found`, `git stash list`, потом доклад** | Объявить потерю, не проверив все источники, — ложная тревога. Проверять: dangling-блобы, stash, `rev-list --all --objects`, бэкап-папки | +| 47 | 🔴 **`session_search` НЕ индексирует Zulip** | Поиск по `set_var`/`9963` вернул **0** при существующем треде на 3380 сообщений. При «читай тред» / «ищи в базе» — **сразу `docker exec zulip-database-1 psql`**, не `session_search`. См. skill `zulip-db-forensics` | +| 48 | 🔴 **Правки проверять в ЦЕЛЕВОМ файле, а не в `/tmp`** | Повтор питфолла 5/34: круг гонялся в `/tmp/final.yml`, а на диске `zont_config/*.yml` остался от прошлой генерации → Alex видел `type: var`, которого в коде **уже нет**. Проверка: `grep -c '<ключ>' <целевой файл>` + `ls -la` mtime | +| 49 | 🔴 **Ключ, добавленный для ОДНОЙ формы, не должен появляться у остальных** | `args` расширен ради формы 2 (`42,0,1`) → повис на **53** записях с мусорным `[0,0]` (круг 33). Правило: `if any(tail)` — писать только ненулевое | +| 50 | 🔴 **Новый разбор, дублирующий принятую форму, обязан её ЗАМЕНИТЬ, а не сосуществовать** | `type: var` вытеснила `set_var` (круг 29) — две формы для одного смысла, новая перебила принятую. Alex: «где блядь `set_var`». Проверять порядок веток в **обеих** точках входа (`emit_action` + инлайн `emit_step`) | +| 51 | 🔴 **`elif` по типу цели недостижим — различать по НАЛИЧИЮ объекта** | `#Z9964=59,'set varname',42,0,1`: `42` — `int`, заходил в объектную ветку, объекта нет → падал в `descr`+`args`. Различать `target in Z_dict`, не `isinstance` | +| 52 | 🔴 **Тело цели разворачивать на месте РОДИТЕЛЯ, но собирать ЕГО строкой** | Родитель несёт `target: `, тело живёт своей строкой под тем же id. Попытка встроить тело в родителя без id ломает обратную сборку (на этом упал потерянный подход, §10.8) | +| 53 | 🔴 **`_body_type` обходить ВЕСЬ YAML, включая вложенные секции** | Плоский обход верхнего уровня не находит объекты в `executors.*` (напр. `9263`) → `unknown event 'on' for object 9263 (type None)`. Нужен рекурсивный `_collect_ids` | > 📌 **Корень аварии — процессный, не технический.** Работа шла **кругами** (34 круга формы), > каждый круг переписывал предыдущий, и **ни один не фиксировался**. При таком режиме любая @@ -1846,14 +1853,95 @@ rm -rf zont_config/_wip > которых в YAML не было. Правильная реакция — **сказать «эта форма не собирается обратно»** > и вернуть `target` в запись, а не изобретать `_target_row()`. +### 10.9. ✅ ВОССТАНОВЛЕНО 2026-09-17 (продолжение сессии) — код написан заново + +> ✅ **Статус: развёртывание РЕАЛИЗОВАНО заново** поверх `b75c51f`. `set_var` (4 формы), +> `param`/`event` (тип 49), `op`/`time`/`days_mask` (тип 50), тела 59 (`objstate`/`expr`/ +> `const`/`var`) — **в рабочей копии, НЕ закоммичено**. Круг `21-13-18` → `749 → 749`, +> строк `774 → 774`, **остаётся 1 расхождение**: `#Z9964` теряет поле 4. + +**Как восстанавливали.** Источник — **Zulip-база**, тред `personal / ZONT Config compiler` +(3380 сообщений). Рабочий отрезок `15:15–15:37` (msg `105060`–`105486`) содержит полный +хронологический лог кругов 27–34: что за форма, где сломалась, какая реплика Alex. + +```bash +docker exec zulip-database-1 psql -U zulip zulip -c " +SELECT m.id, m.date_sent, up.full_name as sender, LEFT(m.content, 900) as content +FROM zerver_message m +JOIN zerver_recipient r ON m.recipient_id = r.id +JOIN zerver_stream s ON r.type_id = s.id +JOIN zerver_userprofile up ON m.sender_id = up.id +WHERE r.type = 2 AND s.name = 'personal' AND m.subject = 'ZONT Config compiler' + AND m.date_sent >= '2026-09-17 15:15:00' +ORDER BY m.date_sent;" +``` + +> 🔴 **Питфолл 47: `session_search` НЕ находит Zulip-тред.** Он индексирует Hermes-сессии +> (`.jsonl`), а не Zulip. Поиск по `set_var`/`9963` вернул **0 результатов**, хотя тред +> существовал и был полон. **При «читай тред» / «ищи в базе» — сразу `docker exec psql`, +> не `session_search`.** (См. skill `zulip-db-forensics`.) + +**Что написано заново** (`config-to-yml.py` ‖ `yml-to-config.py`): + +| Добавлено | Где | Что делает | +|---|---|---| +| `_script_value(oid)` | парсер, внутри `build_yaml` | тело 59 → `{type: objstate\|expr\|const\|var, …}`; незнакомое → None | +| `_set_var_target_body(target)` | парсер | объект 49 → `{type: param, object, event\|param}`; 50 → `{type: time_condition, op, time}` \| `{type: days_mask, days}` | +| `_parse_script_raw(row)` | энкодер | обратный разбор `raw`-строки 59 (для объектов из `raw`-секций) | +| `_script_value_row(node, aid)` | энкодер | собрать строку 59 из полей; `const`→`'N ;#p'`, `expr`→`'expr "%0 + %1"'`, `objstate`→`'objstate N 0 0'`, `var`→`'set <имя>'` + `tail` | +| `_set_var_target_row(sv, aid)` | энкодер | 49/50 из полей; `event`/`param` → код по типу владельца | +| `_set_var_body(node, aid)` | энкодер | `set_var` → строка 59 + регистрация тела цели своей строкой | +| `_body_type(obj_id)` + `_collect_ids` | энкодер | id → тип по всем секциям YAML (включая вложенные `executors.*`) | +| `PARAM_CODES` / `COND_EVENTS` / `_COND_EVENT_GROUP` / `_OP_NAMES` | парсер, внутри `build_yaml` | справочники (см. §10.6) | +| `_PARAM_TO_CODE` / `_EVENT_TO_CODE` / `_EVENT_GROUP` / `_OP_TO_CODE` / `_SEC_TYPE` | энкодер | обратные справочники + карта секция→тип | + +**Ключевое отличие от потерянной версии.** Развёрнутое тело цели **не заменяет** шаг-родитель: +родитель несёт `target: `, а тело живёт **своей строкой** под тем же id. Отсюда требование +восстановить id — то, на чём упал прежний подход (§10.8). Решение: `target` **остаётся в YAML** +как явный ключ, из него энкодер и берёт id. + +**Три бага, найденных при прогоне:** + +| # | Симптом | Причина | Фикс | +|---|---|---|---| +| A | `unknown event 'on' for object 9263 (type None)` | `_body_type` искал только в секциях верхнего уровня; `9263` лежит в `executors` | `_collect_ids` — рекурсивный обход всего YAML | +| B | `set_var needs 'value'…got {…'value': 2}` | ветка «цель-число» стояла **до** ветки «цель-сама-set» | переставлен порядок: `_script_value_row` → затем `var`+`value` | +| C | `id:` уезжал **в конец** записи | `parsed['id'] = step_id` в конце словаря | словарь пересобирается с `id` первым ключом | + +**Остаточный дефект (не закрыт).** `#Z9964=59,'set varname',42,0,1` → собирается `42,0,0` +(теряется поле 4). `9964` **есть в `steps` сценария 8456** с корректным +`set_var: {name: varname, value: 42, args: [0, 1]}` (проверено `yaml.safe_load`), но в +`lines` из steps не попадает — управление до ветки `'set_var' in step` в `emit_step` не доходит, +и строка собирается из другого места без `args`. +**Следующий шаг:** отладка в `emit_step` — печатать, доходит ли управление до `'set_var' in step` +для `9964`, и откуда берётся `42,0,0`. + +> 📌 **Форма, которая читается** (проверено глазами, `grep` по сгенерированному YAML): +> ```yaml +> - id: 9961 +> set_var: {name: var1, type: param, object: 8560, param: target_temp, id: 9960} +> - id: 9963 +> set_var: {name: varname, target: 9962, type: const, value: 2} +> - id: 9964 +> set_var: {name: varname, value: 42, args: [0, 1]} +> - id: 9968 +> set_var: {name: varname, target: 9967, type: expr, expr: '%0 + %1', args: [9965, 9966]} +> - id: 9974 +> set_var: {name: varname, target: 8472, var: var1, value: 0} +> ``` +> `type: var` (круг 29, вытеснявшая `set_var`) — **0 вхождений**. `set_var:` — 53. + +> ✅ **`zont_config/config_local_2026-09-17_21-13-18.yml` ПЕРЕГЕНЕРИРОВАН** в целевой файл +> (не в `/tmp` — питфолл 34/5). Круг `18-43-24` → `661 → 661` ✅ (не затронут). + --- ## 11. Файлы проекта | Файл | Статус | |---|---| -| `config-to-yml.py` | ✅ форма §5: `trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}` — **это и есть состояние `b75c51f`, единственное рабочее** · ⛔ **развёртывание тела 59 (`set_var`/`storeenv`/`log`/`expr`), объекта 50 (`op`/`time`/`days_mask`) и объекта 49 (`param`/`event`) — ПОТЕРЯНО** (§10.8) | -| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 — состояние `b75c51f` · ⛔ **`_script_body()`/`set_var`/`storeenv`/`CMP_CODES`/`PARAM_CODES` — ПОТЕРЯНЫ** (§10.8) | +| `config-to-yml.py` | ✅ форма §5 (`trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}`) **плюс** развёртывание тел (§10.9): `_script_value`, `_set_var_target_body`, `_target_set_name`, `PARAM_CODES`/`COND_EVENTS`/`_OP_NAMES`, разворот орфанов-59 · ⚠️ **не закоммичено** | +| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 **плюс** сборка тел (§10.9): `_parse_script_raw`, `_script_value_row`, `_set_var_target_row`, `_set_var_body`, `_body_type`/`_collect_ids`, `_PARAM_TO_CODE`/`_EVENT_TO_CODE`/`_OP_TO_CODE`/`_SEC_TYPE` · ⚠️ **не закоммичено**, 1 расхождение (`#Z9964`) | | `test_roundtrip.py` | ✅ без изменений (в коммите `199f2b1`) · ✅ **оба круга зелёные после восстановления** | | `zont_config/config_local_2026-09-17_21-13-18.{txt,yml}` | 🆕 **актуальный рабочий снимок** (774 строки, 749 `#Z`) — «Простой тестовый сценарий» `#Z8456` содержит шаги `9925…9990`, `Values test` `#Z9324` не изменился. Круг ✅ `774 → 774`, различий **0** | | `zont_config/config_local_2026-09-17_18-43-24.{txt,yml}` | ↩️ **ВОССТАНОВЛЕН 2026-09-17** из `b75c51f` после ошибочной чистки. Форма `trigger:` + `action:` — 66/66. Круг ✅ `661 → 661` | @@ -1861,10 +1949,12 @@ rm -rf zont_config/_wip | `zont_config/archive/` | `-2`/`-3`/`-4` — закоммичены (`7ae0e32`) | > 📌 **Состояние на 2026-09-17, конец сессии.** HEAD = `b75c51f`, не запушен. -> Оба конвертера — **в состоянии `b75c51f`**, рабочая копия чистая. +> Рабочая копия **содержит восстановленное развёртывание тел** (§10.9) — **не закоммичено**. > Развёртывание тел мини-скриптов и условий (`set_var`, `param`, `event`, `op`/`time`/`days_mask`, -> `expr`/`objstate`/`const`) **существует только в описании** (§10.1–§10.7) — кода нет, делать заново. -> Круг зелёный на обоих снимках: `21-13-18` → `774 → 774`, `18-43-24` → `661 → 661`. +> `expr`/`objstate`/`const`) — **в коде, работает**, остаётся **1 расхождение** по `#Z9964` (поле 4). +> 🔴 **Первым делом следующей сессии — закоммитить** рабочую копию (питфолл 41/44: незакоммиченная +> работа уже терялась один раз). +> Круги: `21-13-18` → `749 → 749` / строк `774 → 774` (1 расхождение), `18-43-24` → `661 → 661` ✅. > ⛔ **`_f2` / `_f5` — МУСОРНЫЕ КЛЮЧИ, удалены из кода (круг 26).** В `config-to-yml.py` был блок