diff --git a/polygon-docs/opus-architecture-audit-prompt.md b/polygon-docs/opus-architecture-audit-prompt.md new file mode 100644 index 0000000..eab5480 --- /dev/null +++ b/polygon-docs/opus-architecture-audit-prompt.md @@ -0,0 +1,136 @@ +# Архитектурный аудит polygon v0.2.5 + +> Адресат: Claude Opus (новый чат) +> Дата: 2026-07-31 +> ⛔ ОТВЕТ — ТОЛЬКО В ЧАТ. Не редактировать файлы. + +--- + +## 1. Что такое polygon + +**Polygon** — отдельный managed-сервис (`polygon.pythonk8s.dev.nubes.ru`), +эмулирующий REST API облачной платформы Nubes. Нужен для интеграционных тестов +приложения **app-autotest** — чтобы тесты гонялись не на реальном облаке, а на +эмуляторе. + +- Flask 3.0 + gunicorn (1 воркер), деплой на Nubes pythonk8s +- Всё состояние в памяти (MockState), без БД +- Data-driven: конфиги сервисов генерируются из 37 STANDS YAML через `from_stands.py` +- 17 API-эндпоинтов с префиксом `/api/v1/svc` +- 19 юнит-тестов (pytest) + 15 интеграционных (в app-autotest) +- Версия: **v0.2.5**, задеплоена и протестирована curl'ом + +## 2. Архитектура + +``` +polygon/ +├── requirements.txt # Flask>=3.0, gunicorn>=21.2, PyYAML>=6.0 +├── tests/ +│ ├── test_converter.py # 10 юнит-тестов from_stands.py +│ └── test_state_machine.py # 9 юнит-тестов state_machine.py +└── site/ + ├── app.py # 57 строк: Flask() + 6 blueprint'ов + app.run() + ├── routes/ + │ ├── root.py # /health, / (Jinja2) + │ ├── services_routes.py# /api/v1/svc/services + │ ├── instances_routes.py# /api/v1/svc/instances + │ ├── operations_routes.py# /api/v1/svc/instanceOperations/* + │ ├── run.py # /api/v1/svc/instanceOperations//run + │ └── mock_routes.py # /api/v1/svc/_mock/* + ├── mock_state.py # MockState: instances, operations, op_params + ├── state_machine.py # apply_effect() — мутация состояния + ├── config/ + │ └── loader.py # SERVICES, OPS_INDEX, DELAY, VERSION + ├── utils/ + │ ├── now.py # now() — UTC ISO с 'Z' + │ └── pluralize.py # pluralize() + ├── from_stands.py # конвертер STANDS YAML → polygon config + ├── static/style.css # тёмная тема + ├── templates/index.html # Jinja2-шаблон + └── services/ # 37 сгенерированных YAML-конфигов +``` + +**Ключевые архитектурные решения:** + +| Решение | Обоснование | +|---------|-------------| +| Blueprint'ы по доменам | По шаблону app-autotest. 6 файлов вместо монолитного app.py (было 418 строк → 57) | +| In-memory state, 1 воркер | Без БД, без гонок. `WEB_CONCURRENCY=1` принудительно | +| Синхронный dtFinish | `sleep(MOCK_OP_DELAY)` прямо в `/run` — без потоков, просто | +| Data-driven из STANDS YAML | 37 сервисов генерится конвертером, не хардкод | +| Модульные переменные в config/loader.py | SERVICES/OPS_INDEX/DELAY/VERSION — единый источник | +| `import config.loader as _cfg` | Модульные переменные меняются на лету (`_cfg.DELAY = x`) | +| CSS/HTML отделены от Python | `static/style.css` + `templates/index.html` — не в f-строках | + +## 3. Поток данных + +``` +STANDS YAML (37 файлов) + → from_stands.py (html.unescape, sub_params→dataDescriptor, stateOut auto-gen) + → services/*.yaml (37 конфигов) + → config/loader.py (_load_all при импорте) + → SERVICES + OPS_INDEX (модульные переменные) + → routes/*.py (читают SERVICES/OPS_INDEX/DELAY) + → MockState (create_instance → create_operation → run → apply_effect) +``` + +## 4. Что сделано (хронология) + +- **Этап 1:** `from_stands.py` — конвертер STANDS → polygon, 37 YAML +- **Этап 2:** `mock_state.py` + `state_machine.py` + `config/loader.py` +- **Этап 3:** `app.py` — 17 эндпоинтов (потом разнесены на blueprint'ы) +- **Code Review #1 (Sonnet):** 12 находок, 2 крит. исправлены (workers=1, KeyError в _merge_params) +- **Decouple:** монолит → blueprint'ы + CSS/HTML разделение +- **Code Review #2 (Sonnet):** 9 находок — `_cfg.DELAY`, VERSION в config/loader, мёртвые импорты +- **Интеграция с app-autotest:** `auth.py` — STANDS-check, v1.2.24 +- **🟡 фиксы:** `json.dumps` вместо ручного JSON, 409 при повторном run, `MOCK_AUTH_TOKEN` +- **Интеграционные тесты:** 15 тестов в app-autotest, все PASS + +## 5. Что проверять + +### Архитектура +- Правильно ли выбран паттерн blueprint'ов? Не переусложнено ли? +- Модульные переменные в `config/loader.py` — адекватный подход или есть лучше? +- In-memory state с 1 воркером — масштабируемо ли для CI (параллельные тесты)? +- Есть ли архитектурные дыры: что будет при 1000 инстансов? При рестарте сервера? + +### Поток данных +- Не теряются ли данные между этапами: STANDS → YAML → loader → state? +- Все ли поля маппятся корректно? (dataDescriptor, valueList, stateOut) +- Правильно ли обрабатываются subresources (create_user, create_database)? + +### API-совместимость +- Все ли форматы ответов совпадают с реальным Nubes API? +- Location-заголовки, пустое тело validate-cfs, 201/404/409 коды? +- Пагинация: стоп по `len < pageSize`, cap 200? + +### Безопасность +- `_mock/*` защищены `MOCK_AUTH_TOKEN` — достаточно? +- Нет ли утечек данных между тестами (autouse reset в conftest)? +- Что будет при отправке невалидного JSON в POST /instances? + +### Что дальше +- Готов ли polygon к CI/CD? +- Что нужно для продакшен-использования (не только тесты)? +- Какие мониторинг/логирование нужны? + +## 6. Вопросы к Опусу + +### Q1. In-memory vs Redis +Сейчас всё в `MockState.instances` (dict). При рестарте сервера всё теряется. +Для CI это ок (тесты стартуют заново). Нужен ли персистентный слой? Redis? Файлы? + +### Q2. Масштабирование +1 воркер, 1 процесс. Если запустить параллельные тесты (несколько pytest-сессий) — +каждая поднимет свой polygon на своём порту? Или один общий сервер? + +### Q3. Генерация YAML +Сейчас `from_stands.py` запускается вручную, результат коммитится. Стоит ли +генерить YAML при старте сервера (если `STANDS_DIR` задан)? + +### Q4. Мониторинг +Нужны ли метрики: количество инстансов, операций, latency? Prometheus-экспорт? +Или только Grafana-логи как сейчас? + +### Q5. Общая оценка +Готов ли polygon к использованию в CI app-autotest? Что критично доделать?