680 lines
15 KiB
Markdown
680 lines
15 KiB
Markdown
# Operations Workspace Contracts
|
||
|
||
## 1. Назначение документа
|
||
|
||
Этот документ задает точные `workspace-scoped` контракты для экранов:
|
||
|
||
- `Operations`
|
||
- `Wizard`
|
||
|
||
Документ нужен как промежуточный слой между:
|
||
|
||
- текущим Alpine UI в `apps/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",
|
||
"target_url": "https://api.example.com/v1/leads",
|
||
"target_action": "POST",
|
||
"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=<optional>`
|
||
|
||
Ответ:
|
||
|
||
- `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.
|