# 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.
