I-1, I-2 финального ревью: старые MCP-сессии возвращали status_labels/transitions_map после миграции; окно до kickstart описано для v8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 KiB
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, у Xwork_kind = "Аналитика",updated_atпрежний; ключейstatus_labelsиtransitions_mapвsettingнет;settings_version = 13;schema_version = 8
Scenario: refresh в очереди при миграции
- WHEN в базе v3 стоит
queuedrefresh {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-1640work_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 агенту как запасной доски для команд без доски в параметрах (поставленных до миграции);
Ключи, которые модуль объявил устаревшими (у jira — status_labels, transitions_map), хаб SHALL удалять при каждом
заполнении умолчаний, не меняя settings_version. 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, процесс со старым кодом (MCP-сессия до reconnect) вставил
status_labels = {}иtransitions_map = {}, затем вызванensure_defaultsтекущего кода (старт хаба, новая MCP-сессия) - THEN ключей в
settingнет,settings_versionпрежний
Scenario: Ключей статуса по факту нет
- WHEN хаб открыл базу v8
- THEN
GET /settingsне содержитstatus_labelsиtransitions_map;GET /agent/settingsагентуjiraих не отдаёт