11 KiB
2026-07-31 — Сессия (v1.1.57 → v1.2.1)
Контекст
Обсуждение архитектуры: унификация ручного и сценарного режимов, гибкие ссылки на инстансы, новый UI редактора сценариев.
Agent consultations
Промпт для Opus
Составлен DOCS/opus-architecture-prompt.md — полное описание проекта, дублирование CREATE-флоу, ограничение instance_map, вопросы по архитектуре.
Ответ Opus — 19 уточняющих вопросов
A. Единый executor (operations/executor.py)
- Граница ответственности: executor делает всё до /run, поллинг — снаружи?
- Обработка ошибок: исключения с типом или dict {ok, error, failed_step}?
- Трекер: вызывать tracker_add внутри executor для create? Нужны ли сценарные инстансы в трекере?
- _finish_op: остаётся в api_test.py или выносим в общий модуль поллинга?
B. Гибкие ссылки на инстансы 5. Приоритет резолвинга: instance_uid > instance_ref > (для create — новый) 6. Где хранить output→uid: в памяти (instance_map) или в scenario_runs.instance_bindings? 7. Валидация _validate_steps: проверять ссылки на output, уникальность, обязательность 8. Обратная совместимость: поддерживать старый формат (service_id) или мигрировать?
C. Схема БД 9. Формат в steps JSONB без изменений схемы? (подтверждено) 10. Использовать существующую колонку scenario_runs.instance_bindings?
D. UI редактора 11. Модал vs inline — рекомендация: модал на весь экран 12. Переиспользовать renderParamRow из operations.js в общем модуле? 13. Pre-fill параметров: авто или по кнопке? 14. Формат параметров: символические имена в БД (как сейчас) или иное?
E. Оптимизация 15. Вынести _find_uid/_uid_from_location в общий utils 16. Механизм восстановления зависших scenario_runs? 17. Ограничение lock_check: только один RUNNING сценарий — осознанное? 18. _op_results в памяти → мигрировать в БД/Redis? 19. Приоритет: бэкенд или UI редактора в первую очередь?
Ключевые решения (отвечено DeepSeek)
A. Executor:
- A1. Граница: до /run включительно, без поллинга. Возвращает {instance_uid, op_uid, display_name}
- A2. Ошибки: dict {ok, error, failed_step}, не исключения
- A3. Трекер: да, tracker_add внутри executor для всех create
- A4. _finish_op: оставить в api_test.py, цикл поллинга → operations/poll.py
B. Ссылки:
- B5. Приоритет: instance_uid > instance_ref > новый create
- B6. Хранение: память + scenario_runs.instance_bindings
- B7. Валидация: проверять ссылки, уникальность, обязательность
- B8. Совместимость: оба формата, без миграции, fallback на service_id
C. БД:
- C9. Без изменений схемы
- C10. Использовать instance_bindings
D. UI:
- D11. Модал на весь экран
- D12. Общий модуль params-render.js
- D13. Авто pre-fill, все параметры с defaults
- D14. Символические имена в БД
E. Оптимизация:
- E15. _find_uid → api/utils.py
- E16. Startup check: TIMEOUT для зависших >1ч
- E17. lock_check оставить
- E18. _op_results не в scope
- E19. Порядок: executor → формат → api_test/scenario → UI
Финальный план (Опус, утверждён)
Сохранён в DOCS/opus-plan-2026-07-31.md. Ветка: opus-architecture-2026-07-31.
4 фазы, 13 шагов:
Фаза 1 — Общие модули:
api/utils.py(NEW) — find_uid(), uid_from_location()operations/poll.py(NEW) — poll_until_done()operations/executor.py(NEW) — execute_operation() до /run, без поллинга
Фаза 2 — Формат шагов:
routes/api_scenario_defs.py— _validate_steps с output/instance_ref/instance_uidoperations/scenario.py— резолвинг instance_uid > instance_ref > service_id
Фаза 3 — Миграция вызывающих:
routes/api_test.py— CMDB delete early return, остальное через executoroperations/scenario.py— через executor + poll_until_donedb/init_db.py— startup cleanup зависших scenario_runs
Фаза 4 — UI редактора:
static/js/params-render.js(NEW) — общий рендер параметровstatic/js/operations.js— использовать params-render.jsstatic/js/scenario-form.js— модальный редакторtemplates/index.html— разметка модала
CMDB delete
Жёсткое удаление через DELETE cmdb-api.deck.nubes.ru/instances/{uid} (без авторизации).
Нужно для недосозданных инстансов (not created). Остаётся в api_test.py, не в executor.
Добавлено после переписки с Георгием Родионовым 29.07.2026.
Реализация (v1.2.0 — v1.2.1)
v1.2.0 — Unified executor + flexible refs
Новые файлы: api/utils.py, operations/poll.py, operations/executor.py, static/js/params-render.js Изменено: api_test.py (через executor + poll), scenario.py (output/ref/bindings), api_scenario_defs.py (_validate_steps), init_db.py (cleanup), operations.js (→ params-render), index.html (load order)
- Дублирование CREATE-флоу устранено: ручной и сценарный → один executor
- instance_uid > instance_ref > service_id (гибкие ссылки)
- Общий поллинг poll_until_done()
- +254 / −277 строк (меньше кода)
v1.2.1 — Opus review fixes
Критическое: tracker_add внутрь executor (защита от сирот, A3) Исправлено: labelCls в renderMapFixedRow, descr с контекстом, порядок скриптов 5 файлов: executor.py, api_test.py, scenario.py, params-render.js, app.py
Проверка в поде (v1.2.1)
- Все 4 новых файла на месте
- 11 JS → 200, порядок правильный
- Schema OK, seed OK, API отвечает
Что дальше
Фаза 4 — модальный редактор сценариев (✅ v1.2.2-v1.2.4):
- ✅ Модал с дропдаунами сервисов/операций
- ✅ output/instance_ref с облачными инстансами
- ✅ Кнопки CRUD крупнее, справка «📖 Как заполнять»
v1.2.16 — instance_meta JSONB
После каждого прогона сохраняется полная информация об инстансе (GET /instances/{uid}). Все поля кроме instanceUid/displayName/svc/serviceId/explainedStatus.
Идеи на будущее (НЕ ДЕЛАТЬ, обдумать)
Context snapshot: сохранять снапшот ВСЕХ инстансов пользователя на момент запуска
(instanceUid, displayName, serviceId, svc, explainedStatus, specification).
При анализе FAIL — видеть контекст: «было 3 running Болванки, возможно конфликт ресурсов».
Хранить в runs.context_snapshot JSONB и scenario_runs.context_snapshot JSONB.
Данные обезличенные, не гигабайты. Отложено до реальной необходимости.
Аудит безопасности GPT-5.3-Codex (2026-07-31)
Проведён полный code review 29 файлов (~6000 строк). Найдено 11 проблем. Результаты зафиксированы в DOCS/ARCHITECTURE.md (раздел 8).
КРИТИЧЕСКИЕ (исправлены)
-
XSS через params в scenario-list.js:119 — k/v параметров в innerHTML без
_esc. Stored XSS через БД сценариев. → v1.2.19 -
JS injection в onclick (scenario-list.js:126-128) —
def.nameв'...'без JS-escape._escне экранирует'→ разрыв строки. → v1.2.19 -
Гонка
_op_results(api_test.py:264-280) — dict без lock, читается/пишется/чистится из нескольких потоков. → v1.2.20:threading.Lock()+pop(k, None) -
Неатомарный lock сценариев (scenario_defs.py + api_scenario_run.py) —
lock_check(SELECT) иINSERT RUNNINGразделены. → v1.2.20:pg_try_advisory_lock
СРЕДНИЕ (исправлены)
-
Lost update трекера (tracker.py) —
_locked_read+_locked_writeв разных lock. → v1.2.19:_atomic_update()под одним lock -
Зависание UI поллинга (scenario-list.js:211) — пустой catch,
busyне сбрасывается. → v1.2.19: счётчик ошибок +stopScenarioPoll+busy=false -
has_targetне проверяется (api_scenario_defs.py:58) — вычисляется и игнорируется. → v1.2.19: явная проверка -
_ensure_schemasilent (pool.py:64) —except Exception: pass. → v1.2.20:traceback.print_exc()
ПОТЕНЦИАЛЬНЫЕ (исправлены)
-
Stale async в редакторе (scenario-form.js) —
loadStepParamsпослеrenderEditorможет перезаписать новый DOM. → v1.2.20:_renderGengeneration token -
validate-cfs хрупкий (terraform.py:250-254) — фильтрация по тексту исключения. → v1.2.20: явный
except json.JSONDecodeError
НЕ ИСПРАВЛЕНО (архитектурное ограничение)
- In-memory
_op_resultsна воркер — не shared между gunicorn-воркерами. Статус иногда читается из API fallback. Решение: Redis/БД для статусов. Отложено — низкая вероятность проблемы на практике (2 воркера, stickiness).