Andrey Epifancev 534e43865b Архив OpenSpec: change-history (#70)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:06:16 +04:00

7.0 KiB
Raw Blame History

ADDED Requirements

Requirement: Приём журнала изменений

Хаб SHALL принимать от агента журнал изменений задач маршрутом POST /agent/history (агентский токен) с телом {command_id, changes: [...]}, элемент — {history_id, seq, key, created, author, field_id, field_name, from, from_string, to, to_string}. До записи хаб SHALL проверить форму всей пачки: changes — список объектов; history_id, key, created, field_name — непустые строки; seq — целое ≥ 0; author, field_id, from, from_string, to, to_string — строка или null. Нарушение — 422 bad_history с номером элемента и именем поля, пачка не применяется. Принятая пачка записывается в issue_change в одной транзакции (см. data-model, «Журнал изменений задач»); ответ — {accepted, duplicates} (записано новых строк, отброшено повторов). Задача элемента может отсутствовать в зеркале — элемент всё равно записывается.

Scenario: Пачка журнала

  • WHEN POST /agent/history с тремя элементами, один из них уже есть в issue_change
  • THEN 200 {accepted: 2, duplicates: 1}

Scenario: Неверный элемент

  • WHEN POST /agent/history с двумя элементами, у второго seq = "0"
  • THEN 422 bad_history с номером элемента 1 и полем seq; ни один элемент не записан

Scenario: Клиентский токен

  • WHEN POST /agent/history клиентским токеном
  • THEN 401

Requirement: Лента изменений

Хаб SHALL отдавать интерфейсам (клиентский токен) ленту событий за период маршрутом GET /changes с параметрами:

  • since, until — ISO-8601 со смещением или Z (время без смещения — 422); без until — текущее время; без since — until минус 24 часа;
  • project — только свои задачи проекта (метка проекта; соседи не входят); неизвестный проект — 404;
  • key — события одной задачи (без ограничения проектом);
  • kinds — через запятую из status, description, dates, comment, other, или all; по умолчанию — status,description,dates,comment;
  • limit — по умолчанию 500, не больше 2000.

События: строки issue_change с at в [since, until] — вид status (поле status), description (поле description), dates (поля duedate, planned_start, planned_end), other (прочие поля); и комментарии issue_comment с created_at в [since, until] — вид comment. Ответ — {since, until, truncated, changes: [...]}, элемент — {at, key, summary, status, kind, field, author, from, to, from_value, to_value}: summary и status — текущие из зеркала (null, если задачи в зеркале нет); from/to — from_string/to_string строки журнала, а если строка null — from_value/to_value; from_value/to_value — сырые значения строки журнала; у комментария field = "comment", from = null, to — текст комментария, from_value = to_value = null. Порядок — по at по убыванию, затем key, history_id, seq (комментарии — comment_id). Событий больше limit — отдаются первые limit по этому порядку и truncated = true. Неверное время, since позже until, неизвестный вид, limit вне 1..2000 — 422 bad_request с именем параметра.

Scenario: Что закрылось за два часа

  • WHEN в 12:00 UTC GET /changes?project=AWPCA&since=2026-10-22T10:00:00Z&kinds=status; в журнале — X (метка AWPCA) «В работе» → «Готово» в 11:15 UTC и Y (метка STOCKS_DRRK) в 11:20 UTC
  • THEN одно событие: X, kind = status, from = "В работе", to = "Готово", автор и at из журнала

Scenario: Комментарий в ленте

  • WHEN у X комментарий с created = "2026-10-22T13:30:00.000+0300" (10:30 UTC), запрос с since 10:00 UTC
  • THEN в ленте событие kind = comment, at = "2026-10-22T10:30:00+00:00", to — текст комментария

Scenario: Прочие поля по запросу

  • WHEN в журнале X сменился исполнитель; запрос без kinds, затем с kinds=all
  • THEN в первом ответе события нет; во втором — kind = other, field = "assignee"

Scenario: Сосед проекта не в ленте

  • WHEN Z без метки AWPCA связана с задачей AWPCA; в журнале Z сменился статус; GET /changes?project=AWPCA
  • THEN события Z нет; GET /changes?key=Z его отдаёт

Scenario: Неверный период

  • WHEN GET /changes?since=вчера
  • THEN 422 bad_request с параметром since

Requirement: Журнал в журнале синхронизаций

GET /sync-runs SHALL отдавать у каждого прогона также history_at (null — журнал по прогону не принят).

Scenario: Прогон с журналом

  • WHEN полный прогон завершился done с принятым журналом; GET /sync-runs
  • THEN у прогона history_at — время приёма; у хвостового refresh {keys} — null

Requirement: История задачи в карточке

GET /issues/{key} SHALL отдавать также changes — все строки issue_change задачи (все виды, включая other) в формате элемента ленты без summary и status, по at по убыванию (строки с at = NULL — в конце); журнала нет — changes = [].

Scenario: Задача с историей

  • WHEN у X в журнале смена статуса вчера и правка описания сегодня; GET /issues/X
  • THEN changes из двух элементов: первым — правка описания (kind = description), вторым — смена статуса