Andrey Epifancev c1b8abada9 Архив OpenSpec: fact-status-removal (#104)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:31:28 +04:00

89 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# modules Specification
## Purpose
Устройство хаба из ядра и подключаемых модулей (первый — `jira`): что принадлежит ядру, как модуль подключается описанием, проверка пересечений при старте и единая схема базы.
## Requirements
### Requirement: Ядро и модули хаба
Хаб SHALL состоять из ядра и подключаемых модулей. Ядро SHALL владеть базой и миграциями, настройками хаба, проектами
(метка, название), хранилищем файлов, реле общей папки, релизом агента, очередью пакетов команд, HTTP-сервером с
авторизацией, раздачей веб-приложения и общим слоем MCP. Модуль SHALL подключаться к ядру описанием: таблицы, типы команд
и их обработчики в очереди, маршруты HTTP, ключи настроек, фоновые работы, инструменты MCP, подкоманды CLI. Первый модуль —
`jira` (зеркало задач, журнал изменений, вложения, команды Jira). Ядро MUST NOT импортировать модули, кроме списка
подключённых модулей и истории миграций схемы.
При старте хаб SHALL проверять, что типы команд, ключи настроек и маршруты (метод и шаблон адреса) ядра и модулей не
пересекаются; пересечение — ошибка старта с указанием конфликта.
Схема базы SHALL оставаться одной, с одной версией `setting.schema_version` на весь хаб; таблицы модуля входят в неё.
#### Scenario: Конфликт типов команд
- **WHEN** два подключённых модуля объявляют тип команды `refresh`
- **THEN** хаб не стартует, ошибка называет тип и оба модуля
#### Scenario: Поведение без изменений
- **WHEN** хаб после перестройки стартует на базе v6 с агентом v14
- **THEN** схема остаётся v6, копия базы не создаётся; адреса API, веба, ответы и инструменты MCP — прежние; агент берёт
команды и отчитывается как раньше
### Requirement: Агенты модулей
У каждого подключённого модуля, объявляющего типы команд, SHALL быть свой агент; имя агента — имя модуля (`jira` —
jira-agent, `wiki` — eva-agent). Агент SHALL определяться по токену: `setting.tokens = {client, agents: {<модуль>:
<токен>}}`, недостающий токен агента хаб создаёт при старте. Хаб SHALL для каждого агента отдельно:
- выдавать в `claim` только команды типов его модуля, с одной командой в полёте на агента (агенты работают
параллельно);
- хранить строку `agent_status` (ключ — имя агента);
- держать опубликованный релиз `release/<модуль>/` и состояние самообновления;
- отдавать в `GET /agent/settings` ключи ядра и ключи для агента только своего модуля;
- пускать на маршруты модуля со scope `agent` только агента этого модуля (токен другого агента — 401).
Миграция v7 SHALL сохранить прежний агентский токен как токен агента `jira`, перенести строку `agent_status` в агента
`jira` и прежнюю публикацию `release/agent.py` в `release/jira/`. jira-agent v14 SHALL работать без обновления.
#### Scenario: jira-agent после выкатки
- **WHEN** хаб v6 с агентским токеном T и публикацией v14 обновлён до v7, jira-agent v14 шлёт `claim` с токеном T
- **THEN** 200; ответ содержит `agent_release {version: 14, …}`; `GET /agent` показывает агента `jira` с версией 14
#### Scenario: Агенты не берут чужие команды
- **WHEN** в очереди пакет 1 с `ensure_issue` и пакет 2 с `wiki_sync`, оба `queued`; агент `wiki` шлёт `claim`
- **THEN** выдан `wiki_sync`; следующий `claim` агента `jira` получает `ensure_issue`, хотя `wiki_sync` ещё `claimed`
#### Scenario: Чужой маршрут агента
- **WHEN** токеном агента `wiki` вызван `POST /agent/state`
- **THEN** 401, зеркало не меняется
#### Scenario: Настройки только своего модуля
- **WHEN** токеном агента `wiki` вызван `GET /agent/settings`
- **THEN** в ответе есть `wiki_pacing` и `settings_version`, ключей jira (`project_key`, `link_types`) нет
### Requirement: Поля проекта от модулей
Проект ядра SHALL состоять из `label`, `name`, `created_at`; остальные поля проекта SHALL объявлять модули: имя, тип,
проверку, изменяемость после создания, подпись и подсказку. Модуль SHALL сам хранить свои поля и отдавать их значения
(включая вычисляемые) для списка проектов. При старте хаб SHALL отвергать два модуля с одним именем поля или поле
модуля с именем поля ядра — ошибка старта с именем поля и модулей. В ядре SHALL NOT оставаться знания о полях модулей
(доска, число задач, корень вики).
#### Scenario: Конфликт полей
- **WHEN** два модуля объявляют поле проекта `root`
- **THEN** хаб не стартует, ошибка называет поле и оба модуля
#### Scenario: Поля jira прежние
- **WHEN** после миграции v7 вызван `GET /projects`
- **THEN** у `AWPCA` `board_id = 4216`, `board_type = scrum`, `issues_count` — как до миграции
### Requirement: Подготовка отчёта команды модулем
Контракт `CommandHooks` SHALL содержать два необязательных хука. `before_result(conn, cmd, status, result, files_dir)`
SHALL вызываться ядром при отчёте по выданной команде до транзакции отчёта, чтобы модуль готовил тяжёлую работу (перевод,
запись файлов) вне блокировки базы; `ValueError` из него SHALL отдаваться как ошибка сервера, а не как неверный статус
отчёта. `abort_result(conn, cmd)` SHALL вызываться, когда отчёт не дошёл до `after_result` (ранний выход, исключение), и
снимать подготовку `before_result`. Модуль без этих хуков SHALL работать как прежде.
#### Scenario: Подготовка до транзакции
- **WHEN** агент шлёт отчёт `done` по команде типа модуля с `before_result`
- **THEN** `before_result` вызван до `BEGIN IMMEDIATE`, после фиксации транзакции — `after_result`
#### Scenario: Отчёт прерван
- **WHEN** `before_result` выполнен, а транзакция отчёта завершилась исключением
- **THEN** вызван `abort_result`, подготовка снята, команда остаётся выданной