11 KiB
MODIFIED Requirements
Requirement: Схема базы хаба
База хаба SHALL быть одним файлом SQLite (WAL) с таблицами issue, issue_link, issue_comment, issue_change, issue_note,
batch, command, sync_run, agent_status, setting, project. Схема SHALL создаваться и мигрироваться самим хабом при
старте по номеру версии в setting.schema_version (текущая — 5). Миграция существующей базы SHALL выполняться в одной
транзакции с блокировкой записи; в той же транзакции, после повторной проверки версии, хаб SHALL скопировать файл базы в
backups/<дата-время>-schema-v<старая>.sqlite рядом с базой — одна копия на миграцию, сколько бы соединений ни
открывалось одновременно. Каталог данных хаба и backups/ SHALL иметь права 0700 (в базе токены): хаб выставляет их
при создании и при каждом старте; если выставить не удалось — предупреждение в лог, работа продолжается. Денежных и
дробных полей нет; даты — ISO-8601 строки в UTC, кроме duedate и плановых дат (даты без времени), строк времени
Jira как есть (issue_comment.created/updated, issue_change.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.
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 = 5
Scenario: Миграция базы v2
- WHEN хаб стартует с базой
schema_version = 2, в которой есть задачи, связи и заметки - THEN в
backups/копия базы v2; добавлена пустаяissue_comment; данные на месте;schema_version = 5
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 = 5
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;schema_version = 5
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
ADDED Requirements
Requirement: Журнал изменений задач
Таблица issue_change SHALL хранить журнал изменений задач Jira по строке на поле записи журнала: history_id (id
записи журнала Jira), seq (номер поля в записи, с 0), key, at (время записи в ISO-8601 UTC, приведённое хабом),
created (время Jira как есть), author, field (логическое имя), field_id, field_name, from_value,
from_string, to_value, to_string, fetched_at; PK (history_id, seq), индексы по at и по (key, at).
Записывать в issue_change SHALL только приём журнала от агента (POST /agent/history); повторно присланная строка с
тем же (history_id, seq) MUST NOT менять сохранённую. HTTP API интерфейсов и MCP MUST NOT писать в issue_change.
Логическое имя field SHALL вычисляться хабом при приёме: status, description, summary, assignee, priority,
duedate, labels, resolution — если так называется field_id, а при его отсутствии — field_name; planned_start,
planned_end — если field_id равен id этого поля в настройке field_ids; epic_link — если field_id равен
epic_link.field_id; иначе — field_name как есть.
Время Jira (created журнала, created комментария) SHALL приводиться к UTC разбором ISO-8601 со смещением вида
+0300, +03:00 или Z, с дробной частью секунд или без; время без смещения считается UTC; неразбираемое — NULL в
приведённой колонке (строка журнала с at = NULL в ленту не попадает).
Таблица issue_comment SHALL хранить также created_at — created, приведённое к UTC, заполняемое при каждой записи
комментариев.
Scenario: Повтор записи журнала
- WHEN агент дважды прислал элемент
{history_id: "5001", seq: 0, key: X, field_name: "status", …} - THEN в
issue_changeодна строка(5001, 0); второй приём ничего не изменил
Scenario: Плановая дата по id поля
- WHEN в настройках
field_ids.planned_end = "customfield_10201", агент прислал элемент сfield_id = "customfield_10201",field_name = "Плановое окончание" - THEN у строки
field = "planned_end"
Scenario: Неизвестное поле
- WHEN агент прислал элемент с
field_id = null,field_name = "Fix Version" - THEN у строки
field = "Fix Version"
Scenario: Время со смещением
- WHEN элемент журнала с
created = "2026-10-22T09:00:00.000+0300" - THEN у строки
at = "2026-10-22T06:00:00+00:00",created— как прислано
Requirement: Отметка журнала в журнале синхронизаций
Таблица sync_run SHALL хранить history_at — время, когда хаб принял итог журнала по этому прогону (NULL — журнал
по прогону не принят). Хаб SHALL проставлять history_at при отчёте полного прогона (refresh по базовой JQL) со
статусом done и result.history без error; у прочих refresh, при failed, cancelled, без result.history или с
history.error — оставлять NULL. history.backfill_error и непустой history.incomplete отметку не держат.
Scenario: Журнал принят
- WHEN
refreshпо базовой JQL завершилсяdoneсresult.history = {issues: 3, changes: 7, incomplete: []} - THEN у его
sync_runпроставленhistory_at
Scenario: Ошибка стартовой загрузки не держит отметку
- WHEN
refreshпо базовой JQL завершилсяdoneсresult.history = {issues: 1, changes: 2, incomplete: [], backfill_error: "HTTP 400 …"} - THEN у его
sync_runпроставленhistory_at
Scenario: Ошибка журнала
- WHEN
refreshзавершилсяdoneсresult.history = {error: "HTTP 500 …"} - THEN у его
sync_runhistory_at = NULL
Scenario: Старый агент
- WHEN агент v12 завершил
refreshdoneбезresult.history - THEN у его
sync_runhistory_at = NULL