diff --git a/README.md b/README.md index 7bf1314..e728264 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Crank - платформа для публикации внешних API в в - `docs/architecture.md` - целевая архитектура системы. - `docs/as-is-to-be.md` - переход `as is -> to be`, page-by-page gap analysis и архитектурные конфликты. - `docs/backend-gap-plan.md` - конкретный backend-план: сущности, API, БД и порядок реализации. +- `docs/operations-workspace-contracts.md` - точные `workspace-scoped` контракты для экранов `Operations` и `Wizard`. - `docs/module-decomposition.md` - декомпозиция crates и модулей. - `docs/data-model.md` - целевая модель данных. - `docs/database-schema.md` - целевая схема БД. diff --git a/TASKS.md b/TASKS.md index c5ba907..ed81a88 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,23 +2,22 @@ ## Current -### `feat/backend-gap-plan` +### `feat/operations-workspace-contracts` Status: completed DoD: -- page-by-page backend gaps переведены в конкретный plan of work -- определены новые сущности, таблицы, API-группы и MCP changes -- зафиксирован порядок реализации по волнам +- `workspace-scoped` контракты для `Operations` и `Wizard` зафиксированы +- определены точные DTO и lifecycle semantics для operations +- `PATCH/DELETE/ARCHIVE` и wizard DTO shape документированы ## Next -- `feat/operations-workspace-contracts` +- `feat/workspace-foundation` ## Backlog -- `feat/operations-workspace-contracts` - `feat/workspace-foundation` - `feat/agent-publishing` - `feat/platform-access` diff --git a/docs/admin-api.md b/docs/admin-api.md index bead6f3..5335840 100644 --- a/docs/admin-api.md +++ b/docs/admin-api.md @@ -136,6 +136,10 @@ - draft generation; - gRPC descriptor upload и discovery. +Детальные DTO и response shapes для экранов `Operations` и `Wizard` зафиксированы отдельно в: + +- `docs/operations-workspace-contracts.md` + ### Agents Нужны: diff --git a/docs/data-model.md b/docs/data-model.md index 2a82bdd..d7cef75 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -80,6 +80,7 @@ - `workspace_id` - `name` - `display_name` +- `category` - `protocol` - `status` - `version` @@ -253,3 +254,7 @@ ## 6. Принцип совместимости Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД. + +Для `Operations` и `Wizard` дополнительный уровень контрактной детализации закреплен в: + +- `docs/operations-workspace-contracts.md` diff --git a/docs/operations-workspace-contracts.md b/docs/operations-workspace-contracts.md new file mode 100644 index 0000000..22c7202 --- /dev/null +++ b/docs/operations-workspace-contracts.md @@ -0,0 +1,677 @@ +# Operations Workspace Contracts + +## 1. Назначение документа + +Этот документ задает точные `workspace-scoped` контракты для экранов: + +- `Operations` +- `Wizard` + +Документ нужен как промежуточный слой между: + +- целевым UI в `test-ui`; +- `docs/backend-gap-plan.md`; +- будущей реализацией `admin-api`. + +## 2. Общие правила + +Базовый префикс: + +```text +/api/admin/workspaces/{workspace_id} +``` + +Общие принципы: + +- все operation принадлежат одному workspace; +- каталог операций отдается целиком с сервера и не требует client-side merge поверх локального mock state; +- wizard работает только через backend и не опирается на `localStorage` или `sessionStorage`; +- versioning остается явным; +- текущий draft lifecycle отделен от published lifecycle; +- `PATCH /operations/{operation_id}` обновляет текущий draft; +- `POST /operations/{operation_id}/versions` создает новый explicit snapshot; +- опубликованная operation не удаляется hard delete, если на нее есть published agent bindings. + +## 3. Канонические представления + +### 3.1. `OperationSummary` + +Используется в каталоге операций. + +```json +{ + "id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "name": "crm_create_lead", + "display_name": "Create Lead", + "protocol": "rest", + "status": "draft", + "category": "sales", + "current_draft_version": 3, + "latest_published_version": 2, + "updated_at": "2026-03-29T12:00:00Z", + "usage_summary": { + "calls_today": 4821, + "error_rate_pct": 1.8, + "avg_latency_ms": 187 + }, + "agent_refs": [ + { + "agent_id": "agent_01hr9g3kgznn57d8s1q0h0g7qn", + "agent_slug": "sales-assistant", + "display_name": "Sales Assistant" + } + ] +} +``` + +### 3.2. `OperationDetail` + +Используется на detail page и как источник для edit-mode wizard. + +```json +{ + "id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "name": "crm_create_lead", + "display_name": "Create Lead", + "protocol": "rest", + "status": "draft", + "category": "sales", + "current_draft_version": 3, + "latest_published_version": 2, + "published_at": "2026-03-28T18:10:00Z", + "draft_version_ref": { + "version": 3, + "status": "draft" + }, + "published_version_ref": { + "version": 2, + "status": "published" + }, + "agent_refs": [ + { + "agent_id": "agent_01hr9g3kgznn57d8s1q0h0g7qn", + "agent_slug": "sales-assistant", + "display_name": "Sales Assistant" + } + ], + "created_at": "2026-03-27T09:15:00Z", + "updated_at": "2026-03-29T12:00:00Z" +} +``` + +### 3.3. `OperationVersionDocument` + +Полная конфигурация версии. Используется wizard и detail page. + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 3, + "status": "draft", + "target": {}, + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {}, + "execution_config": {}, + "tool_description": {}, + "samples": { + "input_json": {}, + "output_json": {} + }, + "generated_draft": { + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {} + }, + "config_export": { + "format_version": "1", + "export_mode": "portable" + }, + "change_note": "update request mapping", + "created_at": "2026-03-29T12:00:00Z", + "updated_at": "2026-03-29T12:45:00Z" +} +``` + +### 3.4. `OperationMutationResult` + +Используется как ответ на create, patch, explicit version, archive и delete. + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 3, + "status": "draft", + "updated_at": "2026-03-29T12:45:00Z" +} +``` + +## 4. Catalog endpoints + +### `GET /api/admin/workspaces/{workspace_id}/operations` + +Назначение: + +- отдать каталог операций для списка. + +Query params: + +- `protocol` +- `status` +- `search` +- `category` +- `agent_id` +- `page` +- `page_size` + +Ответ: + +```json +{ + "items": [ + { + "id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "name": "crm_create_lead", + "display_name": "Create Lead", + "protocol": "rest", + "status": "draft", + "category": "sales", + "current_draft_version": 3, + "latest_published_version": 2, + "updated_at": "2026-03-29T12:00:00Z", + "usage_summary": { + "calls_today": 4821, + "error_rate_pct": 1.8, + "avg_latency_ms": 187 + }, + "agent_refs": [] + } + ], + "page": 1, + "page_size": 20, + "total": 48 +} +``` + +### `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}` + +Назначение: + +- отдать `OperationDetail`. + +### `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions/{version}` + +Назначение: + +- отдать `OperationVersionDocument`. + +## 5. Create and update contracts + +### `POST /api/admin/workspaces/{workspace_id}/operations` + +Назначение: + +- создать operation и draft version `1`. + +Тело: + +```json +{ + "name": "crm_create_lead", + "display_name": "Create Lead", + "protocol": "rest", + "category": "sales", + "target": {}, + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {}, + "execution_config": {}, + "tool_description": {} +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 1, + "status": "draft", + "updated_at": "2026-03-29T12:00:00Z" +} +``` + +### `PATCH /api/admin/workspaces/{workspace_id}/operations/{operation_id}` + +Назначение: + +- обновить identity metadata; +- обновить текущий draft document без создания новой версии вручную со стороны UI. + +Это endpoint для обычного wizard edit-mode. + +Тело: + +```json +{ + "display_name": "Create Lead", + "category": "sales", + "draft_document": { + "target": {}, + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {}, + "execution_config": {}, + "tool_description": {} + } +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 3, + "status": "draft", + "updated_at": "2026-03-29T12:45:00Z" +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions` + +Назначение: + +- создать новую explicit draft-version snapshot. + +Этот endpoint нужен для сценариев: + +- `Save as new version`; +- controlled version history; +- YAML import с `mode=upsert`. + +Тело: + +```json +{ + "source_version": 3, + "change_note": "prepare release candidate", + "document": { + "target": {}, + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {}, + "execution_config": {}, + "tool_description": {} + } +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 4, + "status": "draft", + "updated_at": "2026-03-29T13:05:00Z" +} +``` + +## 6. Lifecycle endpoints + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/publish` + +Назначение: + +- опубликовать конкретную версию операции. + +Тело: + +```json +{ + "version": 3 +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 3, + "status": "published", + "updated_at": "2026-03-29T13:10:00Z" +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/archive` + +Назначение: + +- перевести operation в archived lifecycle state без hard delete. + +Тело: + +```json +{ + "reason": "deprecated by crm_create_lead_v2" +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "version": 3, + "status": "archived", + "updated_at": "2026-03-29T13:15:00Z" +} +``` + +### `DELETE /api/admin/workspaces/{workspace_id}/operations/{operation_id}` + +Назначение: + +- hard delete для еще не опубликованной operation. + +Правила: + +- hard delete допустим только для draft/unpublished operation; +- если operation уже публиковалась, UI должен использовать archive; +- если operation используется published agent-ами, endpoint возвращает `409 Conflict`. + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "deleted": true +} +``` + +## 7. Wizard-specific endpoints + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/test-runs` + +Тело: + +```json +{ + "version": 3, + "input": {} +} +``` + +Ответ: + +```json +{ + "status": "ok", + "request_preview": {}, + "response_preview": {}, + "output": {}, + "runtime_labels": { + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np" + } +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/input-json` + +Тело: + +```json +{ + "version": 3, + "sample": {} +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "version": 3, + "stored": true +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/output-json` + +Тело: + +```json +{ + "version": 3, + "sample": {} +} +``` + +Ответ: + +```json +{ + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "version": 3, + "stored": true +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/drafts/generate` + +Назначение: + +- сгенерировать черновую схему и draft mappings по samples/descriptors. + +Тело: + +```json +{ + "version": 3 +} +``` + +Ответ должен возвращать полный draft payload, пригодный для прямой подстановки в wizard: + +```json +{ + "generated_draft": { + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {} + }, + "input_schema": {}, + "output_schema": {}, + "input_mapping": {}, + "output_mapping": {} +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/descriptors/proto` + +Тело: + +```json +{ + "version": 3, + "file_name": "crm.proto", + "content_b64": "..." +} +``` + +Ответ: + +```json +{ + "descriptor_ref": "desc_01hr9n0m2yzdb8f8xv1gvztm2b", + "stored": true +} +``` + +### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/descriptors/descriptor-set` + +Тело: + +```json +{ + "version": 3, + "file_name": "crm-descriptor-set.bin", + "content_b64": "..." +} +``` + +Ответ: + +```json +{ + "descriptor_ref": "desc_01hr9n0m2yzdb8f8xv1gvztm2b", + "stored": true +} +``` + +### `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/grpc/services?version=3` + +Ответ: + +```json +{ + "services": [ + { + "package": "crm.v1", + "service": "LeadService", + "methods": [ + { + "name": "CreateLead", + "kind": "unary", + "input_schema": {}, + "output_schema": {} + } + ] + } + ] +} +``` + +## 8. YAML contracts + +### `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/export` + +Query: + +- `mode=portable|bundle` +- `version=` + +Ответ: + +- `application/yaml` + +### `POST /api/admin/workspaces/{workspace_id}/operations/import` + +Query: + +- `mode=create|upsert` + +Body: + +- raw YAML document + +Ответ: + +```json +{ + "workspace_id": "ws_01hr9dzv4s5ec1f1v3y8t1n7gr", + "operation_id": "op_01hr9f7q4b0w1svxv5a6j8k2np", + "version": 4, + "status": "draft", + "result": "upserted" +} +``` + +## 9. Wizard lifecycle semantics + +### Текущее редактирование + +Wizard всегда открывается на current draft version и использует: + +- `GET /operations/{operation_id}` +- `GET /operations/{operation_id}/versions/{current_draft_version}` +- `PATCH /operations/{operation_id}` + +Это основной сценарий редактирования. + +### Явное создание новой версии + +Если UI вводит действие `Save as new version`, оно должно использовать: + +- `POST /operations/{operation_id}/versions` + +Это уже отдельный snapshot, а не обычное сохранение формы. + +### Публикация + +Публикуется конкретная version, а не “текущее состояние формы”. + +Поэтому UI всегда должен передавать: + +- `version` + +в `POST /publish`. + +## 10. Конфликты, которые закрывает этот документ + +### Конфликт 1. `PATCH` против explicit version snapshots + +Решение: + +- wizard использует `PATCH` для текущего draft; +- controlled snapshots остаются на `POST /versions`. + +### Конфликт 2. Delete semantics + +Решение: + +- hard delete только для unpublished drafts; +- для опубликованных операций использовать archive. + +### Конфликт 3. Category source of truth + +Решение: + +- `category` признается частью operation identity metadata и хранится на стороне backend. + +### Конфликт 4. Catalog data merge + +Решение: + +- каталог должен возвращать все нужные поля для UI с сервера; +- локальные overlays и tombstones в целевой реализации не используются. + +## 11. Следующий шаг + +После этого документа следующая реализация должна идти в таком порядке: + +1. обновить `admin-api` handlers и service contracts под workspace prefix; +2. добавить `category`, `workspace_id` и update/delete/archive lifecycle в storage model; +3. перевести wizard на create/patch/version semantics из этого документа; +4. только после этого подключать `Operations` и `Wizard` к реальному UI.