147 lines
14 KiB
Markdown
147 lines
14 KiB
Markdown
## MODIFIED Requirements
|
||
|
||
### Requirement: Схема базы хаба
|
||
База хаба SHALL быть одним файлом SQLite (WAL) с таблицами `issue`, `issue_link`, `issue_comment`, `issue_change`, `issue_attachment`,
|
||
`attachment_file`, `issue_note`, `batch`, `command`, `sync_run`, `agent_status`, `setting`, `project`. Схема SHALL создаваться и мигрироваться самим хабом при
|
||
старте по номеру версии в `setting.schema_version` (текущая — 8). Миграция существующей базы SHALL выполняться в одной
|
||
транзакции с блокировкой записи; в той же транзакции, после повторной проверки версии, хаб SHALL скопировать файл базы в
|
||
`backups/<дата-время>-schema-v<старая>.sqlite` рядом с базой — одна копия на миграцию, сколько бы соединений ни
|
||
открывалось одновременно. Каталог данных хаба и `backups/` SHALL иметь права `0700` (в базе токены): хаб выставляет их
|
||
при создании и при каждом старте; если выставить не удалось — предупреждение в лог, работа продолжается. Денежных и
|
||
дробных полей нет; даты — ISO-8601 строки в UTC, кроме `duedate` и плановых дат (даты без времени), строк времени
|
||
Jira как есть (`issue_comment.created`/`updated`, `issue_change.created`, `issue_attachment.created`) и значений полей журнала.
|
||
|
||
Миграция v3 → v4 SHALL: создать `project` и колонку `batch.project` (у существующих пакетов `NULL`); разобрать прежнюю
|
||
настройку `base_jql` вида `… labels = <M>` или `… labels in (<M1>, …)` и завести проекты с этими метками (название проекта
|
||
`AWPCA` — «АРМ КА», остальных — метка; `board_type = NULL`); отдать прежний `board_id` проекту, если проект один, иначе
|
||
никому, с предупреждением в лог; у строк `sync_run` с `query`, равной прежней `base_jql`, и у команд `refresh` в
|
||
`queued`/`claimed` с `params.jql`, равной прежней `base_jql`, записать базовую JQL, вычисленную из проектов. Ключи
|
||
`base_jql` и `board_id` в `setting` миграция MUST NOT удалять: базу может открыть процесс новой версии раньше, чем
|
||
перезапущен хаб прежней версии, который их читает. Не разобранная `base_jql` — проектов нет, предупреждение в лог с
|
||
прежним значением.
|
||
|
||
Миграция v4 → v5 SHALL: создать `issue_change` с индексами; добавить колонки `issue_comment.created_at` и
|
||
`sync_run.history_at`; заполнить `issue_comment.created_at` у имеющихся комментариев приведением `created` к UTC (не
|
||
разобралось — `NULL`). У имеющихся `sync_run` `history_at = NULL`.
|
||
|
||
Миграция v5 → v6 SHALL: создать пустые `issue_attachment` (с индексом по `key`) и `attachment_file`. Списки вложений
|
||
появляются с первым `refresh` агента v14.
|
||
|
||
Миграция v7 → v8 SHALL: удалить колонку `issue_note.fact_status` (если она есть; значения удаляются безвозвратно, копия —
|
||
в `backups/` по общему правилу) и ключи `status_labels` и `transitions_map` из `setting`, если они есть, и поднять
|
||
`settings_version`, если хотя бы один из ключей был удалён. Прочие поля заметок, задачи и настройки миграция MUST NOT
|
||
менять.
|
||
|
||
#### Scenario: Первый запуск
|
||
- **WHEN** хаб стартует с несуществующим файлом базы
|
||
- **THEN** файл создаётся, таблицы и индексы на месте, `setting.schema_version` = текущая; каталог данных с правами `0700`
|
||
|
||
#### Scenario: Миграция базы v1
|
||
- **WHEN** хаб стартует с базой `schema_version = 1`, в которой есть задачи, заметки и команда в `claimed`
|
||
- **THEN** в `backups/` копия базы v1; добавлена `command.deliveries` (у `claimed` — 1, у остальных — 0); добавлена пустая
|
||
`issue_comment`; данные на месте; `schema_version` = текущая
|
||
|
||
#### Scenario: Миграция базы v2
|
||
- **WHEN** хаб стартует с базой `schema_version = 2`, в которой есть задачи, связи и заметки
|
||
- **THEN** в `backups/` копия базы v2; добавлена пустая `issue_comment`; данные на месте; `schema_version` = текущая
|
||
|
||
#### Scenario: Миграция базы v3
|
||
- **WHEN** хаб стартует с базой `schema_version = 3`, `base_jql = "project = AURORA AND labels = AWPCA"`, `board_id = 4216`,
|
||
в `sync_run` полный прогон с этой `query`
|
||
- **THEN** в `backups/` копия базы v3; проект `AWPCA` «АРМ КА» с `board_id = 4216`, `board_type = NULL`; ключи `base_jql`
|
||
и `board_id` в `setting` на месте; `query` того прогона — `project = AURORA AND labels in ("AWPCA")`; задачи, заметки,
|
||
пакеты на месте; `schema_version` = текущая
|
||
|
||
#### Scenario: Миграция базы v4
|
||
- **WHEN** хаб стартует с базой `schema_version = 4`, у задачи X комментарий с `created = "2026-10-22T13:05:11.000+0300"`,
|
||
в `sync_run` полный прогон
|
||
- **THEN** в `backups/` копия базы v4; есть пустая `issue_change`; у комментария `created_at = "2026-10-22T10:05:11+00:00"`,
|
||
`created` прежний; у прогона `history_at = NULL`; затем миграция v5 → v6; `schema_version` = текущая
|
||
|
||
#### Scenario: Миграция базы v5
|
||
- **WHEN** хаб стартует с базой `schema_version = 5`
|
||
- **THEN** в `backups/` копия базы v5; есть пустые `issue_attachment` и `attachment_file`; прочие таблицы не тронуты;
|
||
`schema_version` = текущая
|
||
|
||
#### Scenario: Миграция базы v7
|
||
- **WHEN** хаб стартует с базой `schema_version = 7`, у задачи X заметка `fact_status = "В работе"`, `work_kind =
|
||
"Аналитика"`, в `setting` есть `status_labels` и `transitions_map`, `settings_version = 12`
|
||
- **THEN** в `backups/` копия базы v7; в `issue_note` нет колонки `fact_status`, у X `work_kind = "Аналитика"`,
|
||
`updated_at` прежний; ключей `status_labels` и `transitions_map` в `setting` нет; `settings_version = 13`;
|
||
`schema_version = 8`
|
||
|
||
#### Scenario: refresh в очереди при миграции
|
||
- **WHEN** в базе v3 стоит `queued` `refresh {jql: "project = AURORA AND labels = AWPCA"}`
|
||
- **THEN** после миграции его `params.jql` — `project = AURORA AND labels in ("AWPCA")`, и по завершении он считается
|
||
полным прогоном
|
||
|
||
#### Scenario: Два соединения к базе v1
|
||
- **WHEN** к базе `schema_version = 1` одновременно открываются два соединения
|
||
- **THEN** миграция выполнена один раз, в `backups/` одна копия `schema-v1`
|
||
|
||
#### Scenario: Открытый каталог данных
|
||
- **WHEN** хаб стартует, каталог данных существует с правами `0755`
|
||
- **THEN** права каталога и `backups/` — `0700`
|
||
|
||
### Requirement: Служебные заметки
|
||
Таблица `issue_note` SHALL хранить поля, которых нет в Jira: `key` (PK; ключ Jira или временный `new:<n>`),
|
||
`work_kind`, `done_text`, `rest_text`, `owner_note`, `updated_at`, `updated_by`. Обновление зеркала
|
||
MUST NOT менять `issue_note`. При завершении `ensure_issue` с временным ключом хаб SHALL перенести заметку на настоящий
|
||
ключ — в том числе по позднему отчёту `done` с ключом о команде, отменённой или переведённой в `failed` до отчёта. Если
|
||
под настоящим ключом заметка уже есть, хаб SHALL слить их: каждое поле берётся из временной заметки, если там оно
|
||
непустое, иначе остаётся под настоящим ключом; `updated_at`/`updated_by` — от более поздней; временная строка удаляется.
|
||
|
||
#### Scenario: Заметка переживает refresh
|
||
- **WHEN** у задачи есть `issue_note.work_kind = "Аналитика"` и приходит полное обновление зеркала
|
||
- **THEN** `work_kind` не изменился
|
||
|
||
#### Scenario: Слияние с заметкой под ключом
|
||
- **WHEN** у `new:5` заметка `work_kind = "Аналитика"`, `owner_note` пусто; у `AURORA-1640` заметка `owner_note = "спросить ИБ"`;
|
||
`ensure_issue` для `new:5` завершилась с `AURORA-1640`
|
||
- **THEN** у `AURORA-1640` `work_kind = "Аналитика"` и `owner_note = "спросить ИБ"`; строки `new:5` нет
|
||
|
||
### Requirement: Настройки
|
||
Таблица `setting` SHALL хранить парами ключ–JSON: `project_key`, `field_ids`, `issue_types`, `priority_map`, `link_types`,
|
||
`pacing`, `tokens` (агентский и клиентский), `settings_version` (увеличивается при любой
|
||
правке), `backup_keep`, `auto_refresh_minutes`, `relay_dir` (абсолютный путь папки реле или `null`, по умолчанию `null`;
|
||
относительный путь и не-строка при записи — 422; агенту не передаётся), `closed_statuses` (список имён статусов Jira, считающихся закрытыми, по умолчанию
|
||
`["Готово", "Снято"]`; не-список при записи — 422; агенту не передаётся; на существующей базе появляется со значением
|
||
по умолчанию при открытии хаба). Базовая JQL вычисляется из проектов, доска — свойство проекта (требование
|
||
«Проекты»); на новой базе ключей `base_jql` и `board_id` нет. Оставшиеся от v3 ключи хаб SHALL NOT использовать, кроме
|
||
передачи `board_id` агенту как запасной доски для команд без доски в параметрах (поставленных до миграции);
|
||
`GET /settings` SHALL отдавать вычисленную `base_jql` только для чтения, а запись `base_jql` или `board_id` через
|
||
`PUT /settings` — 422 с пояснением. Спринты агент читает с досок проектов, отдельной настройки имён спринтов нет. Начальные значения SHALL импортироваться из `admin/jira-sync/config.example.json`
|
||
командой `sdlc-hub init`; пароль и логин Jira в настройках MUST NOT присутствовать. `issue_types` — объект
|
||
`{task, epic, bug}`, у каждого `{name, id}` (`id` может быть `null`); `init` берёт их из ключей конфига
|
||
`issue_type_<тип>` и `issue_type_<тип>_id`; `bug` попадает в `issue_types`, только если в конфиге есть хотя бы
|
||
один из ключей `issue_type_bug`, `issue_type_bug_id` (`task` и `epic` — как раньше, всегда).
|
||
|
||
#### Scenario: Правка настроек поднимает версию
|
||
- **WHEN** через API изменён `pacing`
|
||
- **THEN** `settings_version` увеличен, следующий `claim` отдаёт агенту новую версию
|
||
|
||
#### Scenario: Конфиг без ключей ошибки
|
||
- **WHEN** `sdlc-hub init --config` с `issue_type_task`, `issue_type_epic`, без `issue_type_bug`
|
||
- **THEN** `issue_types` содержит `task` и `epic`, `bug` нет; постановка `ensure_issue` с `issue_type = bug` — 422
|
||
|
||
#### Scenario: Неверный relay_dir
|
||
- **WHEN** `PUT /settings` с `relay_dir = "rshb/jira-bridge"`
|
||
- **THEN** 422, значение не записано, `settings_version` не изменился
|
||
|
||
#### Scenario: Закрытые статусы по умолчанию
|
||
- **WHEN** хаб открыл базу, в которой нет ключа `closed_statuses`
|
||
- **THEN** `GET /settings` отдаёт `closed_statuses = ["Готово", "Снято"]`; `claim` агенту этого ключа не передаёт
|
||
|
||
#### Scenario: Запись базовой JQL
|
||
- **WHEN** `PUT /settings {base_jql: "project = AURORA"}`
|
||
- **THEN** 422 «базовая JQL собирается из проектов»; `settings_version` не изменился
|
||
|
||
#### Scenario: Создание проекта не трогает версию настроек
|
||
- **WHEN** создан проект `STOCKS_DRRK`
|
||
- **THEN** `settings_version` прежний; `GET /settings` отдаёт `base_jql` с обеими метками
|
||
|
||
#### Scenario: Ключей статуса по факту нет
|
||
- **WHEN** хаб открыл базу v8
|
||
- **THEN** `GET /settings` не содержит `status_labels` и `transitions_map`; `GET /agent/settings` агенту `jira` их не
|
||
отдаёт
|