2026-10-14 16:00:57 +04:00

92 lines
8.5 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.

## MODIFIED Requirements
### Requirement: Процесс и доступ
`jira-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:<port>` (по умолчанию 8790), база и настройки в
`~/.local/share/jira-hub/` (переопределяется `JIRA_HUB_HOME`). Все маршруты API SHALL требовать
`Authorization: Bearer <token>`: агентский токен открывает только `/agent/*`, клиентский — всё остальное. Исключение —
страница доски связей (`GET /`, `GET /links`): это статический файл без данных, он отдаётся без токена. Ответы API —
JSON, ошибки — `{error, detail}` с кодами 400 (неверное тело или `Content-Length`), 401, 404, 409, 413 (тело больше
предела), 422, 500 (внутренняя ошибка); реле общей папки дополнительно отвечает 502. Методы, кроме GET, POST и PUT,
отвечают 501 стандартной страницей `http.server`, не JSON.
#### Scenario: Чужой токен
- **WHEN** агентским токеном вызван `GET /issues`
- **THEN** 401
#### Scenario: Неподдерживаемый метод
- **WHEN** `DELETE /issues/AURORA-1`
- **THEN** 501, тело не JSON; база не меняется
#### Scenario: Страница без токена
- **WHEN** `GET /` без заголовка `Authorization`
- **THEN** 200, `Content-Type: text/html; charset=utf-8`, тело — страница доски связей; данных зеркала в ней нет
## ADDED Requirements
### Requirement: Снимок доски
Хаб SHALL предоставлять `GET /board` под клиентским токеном — согласованный снимок зеркала для доски связей, прочитанный
в одной транзакции:
- `generated_at` — момент снимка (UTC);
- `last_sync` — последний завершённый полный прогон (`refresh` по базовой JQL): `{started_at, finished_at, issues_count}`
или `null`, если такого не было;
- `issues` — все задачи зеркала, включая эпики и соседей: `key`, `summary`, `issue_type`, `status`, `priority`,
`assignee`, `sprint`, `duedate`, `planned_start`, `planned_end`, `epic_key`, `labels` (список), `neighbor` (bool),
`fact_status`, `work_kind` (из заметок, `null` если заметки нет); порядок — по ключу;
- `links` — все связи зеркала: `{source, target, link_type}`, `link_type` — имя типа в Jira, направление как в
`issue_link` (`source` блокирует/ссылается на `target`).
Перевод в модель доски (вид работы по меткам, имена эпиков, логические типы связей) — дело интерфейса; хаб отдаёт данные
зеркала без преобразований.
#### Scenario: Снимок с задачами и связью
- **WHEN** в зеркале эпик AURORA-1351, задачи AURORA-1620 (метка `kind-dev`, эпик AURORA-1351) и AURORA-1621, связь
`AURORA-1620 → AURORA-1621` типа `ASUPR_Блокер_Начала`, заметка `work_kind = "разработка"` у AURORA-1620
- **THEN** `GET /board` отдаёт три задачи по ключу, у AURORA-1620 `labels = ["kind-dev"]`, `epic_key = "AURORA-1351"`,
`work_kind = "разработка"`; в `links` — `{source: "AURORA-1620", target: "AURORA-1621", link_type: "ASUPR_Блокер_Начала"}`
#### Scenario: Последний полный прогон
- **WHEN** в журнале синхронизаций полный прогон, завершённый в 10:00, и после него обновление по ключам в 11:00
- **THEN** `last_sync.finished_at` — 10:00
#### Scenario: Без токена
- **WHEN** `GET /board` без токена или с агентским токеном
- **THEN** 401
### Requirement: Страница доски связей
Хаб SHALL отдавать по `GET /` и `GET /links` страницу доски связей — один HTML-файл из пакета хаба
(`src/jira_hub/web/links.html`, развитие просмотрщика «Спринт 2 линк»). Страница SHALL:
- спрашивать клиентский токен хаба при первом открытии и после ответа 401, хранить его в `localStorage` браузера и
передавать в заголовке `Authorization` своих запросов к API того же хоста;
- при открытии загружать `GET /board` и строить доску из него: задачи-эпики — дорожки, остальные задачи (включая
соседей) — карточки; вид работы — по меткам `kind-*`, иначе по типу задачи; срок карточки — `planned_end`, при его
отсутствии `duedate`; связи — по известным странице типам Jira, неизвестные типы пропускаются;
- показывать в шапке время последнего полного прогона (`last_sync.finished_at`);
- по кнопке «Обновить из Jira» ставить `POST /refresh {full: true, author: "viewer"}`, показывать ход пакета по
`GET /batches/{id}` до завершения и затем перечитывать `GET /board`; если агент офлайн (`GET /agent`), предупреждать,
но пакет ставить; при ошибке пакета — показывать её текст;
- по кнопке «Перечитать» загружать `GET /board` заново без обращения к Jira;
- сохранять раскладку карточек, порядок и свёртку дорожек, фильтры в `localStorage` под ключами, не зависящими от
источника данных; импорт JSON, выгруженного страницей, в режиме хаба SHALL сохранять эти настройки и сразу
перечитывать данные хаба, а не показывать снимок из файла;
- оставлять загрузку CSV из фильтра Jira как запасной путь (снимок с пометкой «из файла»; «Перечитать» возвращает данные
хаба);
- при недоступном хабе показывать понятное сообщение и предлагать загрузить файл; встроенной выгрузки данных страница
не содержит.
#### Scenario: Первый вход
- **WHEN** страница открыта впервые, токена в `localStorage` нет
- **THEN** вместо доски — поле клиентского токена с подсказкой `uv run jira-hub tokens`; после ввода верного токена доска
строится из `GET /board`, токен сохранён
#### Scenario: Неверный токен
- **WHEN** сохранённый токен устарел и `GET /board` ответил 401
- **THEN** страница снова показывает поле токена с пояснением, что токен не подошёл
#### Scenario: Обновление из Jira
- **WHEN** нажата «Обновить из Jira», агент на связи
- **THEN** в хабе пакет из одного `refresh` по базовой JQL с автором `viewer`; страница показывает «пакет #N выполняется»,
по завершении перечитывает доску и показывает новое время данных
#### Scenario: Перенос раскладки
- **WHEN** в режиме хаба импортирован JSON, выгруженный старой страницей «Спринт 2 линк»
- **THEN** раскладка, дорожки и фильтры из JSON применены к задачам хаба; задачи и связи — из хаба, не из JSON