chore(openspec): архив sdlc-rename, Purpose спек hub-api, mcp, web-app под sdlc-hub (#97)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
93bb968235
commit
b92d5e8582
@ -195,7 +195,7 @@ SHALL обнулить `issues_count` и `links_count` её `sync_run` (`started
|
|||||||
передачи `board_id` агенту как запасной доски для команд без доски в параметрах (поставленных до миграции);
|
передачи `board_id` агенту как запасной доски для команд без доски в параметрах (поставленных до миграции);
|
||||||
`GET /settings` SHALL отдавать вычисленную `base_jql` только для чтения, а запись `base_jql` или `board_id` через
|
`GET /settings` SHALL отдавать вычисленную `base_jql` только для чтения, а запись `base_jql` или `board_id` через
|
||||||
`PUT /settings` — 422 с пояснением. Спринты агент читает с досок проектов, отдельной настройки имён спринтов нет. Начальные значения SHALL импортироваться из `admin/jira-sync/config.example.json`
|
`PUT /settings` — 422 с пояснением. Спринты агент читает с досок проектов, отдельной настройки имён спринтов нет. Начальные значения SHALL импортироваться из `admin/jira-sync/config.example.json`
|
||||||
командой `jira-hub init`; пароль и логин Jira в настройках MUST NOT присутствовать. `issue_types` — объект
|
командой `sdlc-hub init`; пароль и логин Jira в настройках MUST NOT присутствовать. `issue_types` — объект
|
||||||
`{task, epic, bug}`, у каждого `{name, id}` (`id` может быть `null`); `init` берёт их из ключей конфига
|
`{task, epic, bug}`, у каждого `{name, id}` (`id` может быть `null`); `init` берёт их из ключей конфига
|
||||||
`issue_type_<тип>` и `issue_type_<тип>_id`; `bug` попадает в `issue_types`, только если в конфиге есть хотя бы
|
`issue_type_<тип>` и `issue_type_<тип>_id`; `bug` попадает в `issue_types`, только если в конфиге есть хотя бы
|
||||||
один из ключей `issue_type_bug`, `issue_type_bug_id` (`task` и `epic` — как раньше, всегда).
|
один из ключей `issue_type_bug`, `issue_type_bug_id` (`task` и `epic` — как раньше, всегда).
|
||||||
@ -205,7 +205,7 @@ SHALL обнулить `issues_count` и `links_count` её `sync_run` (`started
|
|||||||
- **THEN** `settings_version` увеличен, следующий `claim` отдаёт агенту новую версию
|
- **THEN** `settings_version` увеличен, следующий `claim` отдаёт агенту новую версию
|
||||||
|
|
||||||
#### Scenario: Конфиг без ключей ошибки
|
#### Scenario: Конфиг без ключей ошибки
|
||||||
- **WHEN** `jira-hub init --config` с `issue_type_task`, `issue_type_epic`, без `issue_type_bug`
|
- **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
|
- **THEN** `issue_types` содержит `task` и `epic`, `bug` нет; постановка `ensure_issue` с `issue_type = bug` — 422
|
||||||
|
|
||||||
#### Scenario: Неверный relay_dir
|
#### Scenario: Неверный relay_dir
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
# hub-api Specification
|
# hub-api Specification
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
HTTP API хаба `jira-hub serve`: маршруты агента (выдача команд, отчёты, загрузка состояния), маршруты интерфейсов (зеркало, заметки, пакеты, настройки), резервные копии, автообновление зеркала и реле общей папки для агента за VPN.
|
HTTP API хаба `sdlc-hub serve`: маршруты агента (выдача команд, отчёты, загрузка состояния), маршруты интерфейсов (зеркало, заметки, пакеты, настройки), резервные копии, автообновление зеркала и реле общей папки для агента за VPN.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Процесс и доступ
|
### Requirement: Процесс и доступ
|
||||||
`jira-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:<port>` (по умолчанию 8790), база и настройки в
|
`sdlc-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:<port>` (по умолчанию 8790), база и настройки в
|
||||||
`~/.local/share/jira-hub/` (переопределяется `JIRA_HUB_HOME`). Все маршруты API SHALL требовать
|
`~/.local/share/sdlc-hub/` (переопределяется `SDLC_HUB_HOME`). Все маршруты API SHALL требовать
|
||||||
`Authorization: Bearer <token>`: токен агента модуля открывает только `/agent/*` (маршруты ядра — от имени этого агента,
|
`Authorization: Bearer <token>`: токен агента модуля открывает только `/agent/*` (маршруты ядра — от имени этого агента,
|
||||||
маршруты модуля со scope `agent` — только агенту своего модуля, см. `modules`, «Агенты модулей»), клиентский — всё
|
маршруты модуля со scope `agent` — только агенту своего модуля, см. `modules`, «Агенты модулей»), клиентский — всё
|
||||||
остальное. `jira-hub tokens` SHALL печатать клиентский токен и токены агентов с именами модулей. Исключение —
|
остальное. `sdlc-hub tokens` SHALL печатать клиентский токен и токены агентов с именами модулей. Исключение —
|
||||||
раздача веб-приложения (`GET /app`, `GET /app/…`) и перенаправления `GET /`, `GET /links`: это статические файлы без
|
раздача веб-приложения (`GET /app`, `GET /app/…`) и перенаправления `GET /`, `GET /links`: это статические файлы без
|
||||||
данных, они отдаются без токена. Ответы API — JSON, ошибки — `{error, detail}` с кодами 400 (неверное тело или
|
данных, они отдаются без токена. Ответы API — JSON, ошибки — `{error, detail}` с кодами 400 (неверное тело или
|
||||||
`Content-Length`), 401, 404, 409, 413 (тело больше предела), 422, 500 (внутренняя ошибка); реле общей папки
|
`Content-Length`), 401, 404, 409, 413 (тело больше предела), 422, 500 (внутренняя ошибка); реле общей папки
|
||||||
@ -30,7 +30,7 @@ JSON.
|
|||||||
- **THEN** 200, `Content-Type: text/html; charset=utf-8`, тело — `index.html` сборки; данных зеркала в нём нет
|
- **THEN** 200, `Content-Type: text/html; charset=utf-8`, тело — `index.html` сборки; данных зеркала в нём нет
|
||||||
|
|
||||||
#### Scenario: Токены после миграции
|
#### Scenario: Токены после миграции
|
||||||
- **WHEN** база v6 с агентским токеном T мигрировала в v7, запущен `jira-hub tokens`
|
- **WHEN** база v6 с агентским токеном T мигрировала в v7, запущен `sdlc-hub tokens`
|
||||||
- **THEN** вывод содержит клиентский токен, `jira: T` и новый токен `wiki`
|
- **THEN** вывод содержит клиентский токен, `jira: T` и новый токен `wiki`
|
||||||
|
|
||||||
### Requirement: Маршруты агента
|
### Requirement: Маршруты агента
|
||||||
@ -249,7 +249,7 @@ SHALL скопировать файл базы в `backups/<дата-время>
|
|||||||
- **THEN** поставлен один `refresh` с JQL `project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")`
|
- **THEN** поставлен один `refresh` с JQL `project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")`
|
||||||
|
|
||||||
### Requirement: Реле через общую папку
|
### Requirement: Реле через общую папку
|
||||||
`jira-hub serve` SHALL запускать поток реле вместе с HTTP-сервером. Пока настройка `relay_dir` пуста (`null`), реле
|
`sdlc-hub serve` SHALL запускать поток реле вместе с HTTP-сервером. Пока настройка `relay_dir` пуста (`null`), реле
|
||||||
бездействует; настройку оно SHALL перечитывать не реже раза в 5 с, включение и выключение не требуют перезапуска. Если
|
бездействует; настройку оно SHALL перечитывать не реже раза в 5 с, включение и выключение не требуют перезапуска. Если
|
||||||
каталог `relay_dir` существует, реле SHALL создать в нём подкаталоги `req/`, `resp/`, `work/`; если не существует —
|
каталог `relay_dir` существует, реле SHALL создать в нём подкаталоги `req/`, `resp/`, `work/`; если не существует —
|
||||||
записать одно предупреждение в лог и проверять снова.
|
записать одно предупреждение в лог и проверять снова.
|
||||||
@ -340,10 +340,10 @@ SHALL скопировать файл базы в `backups/<дата-время>
|
|||||||
- **THEN** 401
|
- **THEN** 401
|
||||||
|
|
||||||
### Requirement: Публикация версии агента
|
### Requirement: Публикация версии агента
|
||||||
`jira-hub agent publish [--module <модуль>] PATH` SHALL публиковать файл агента модуля (по умолчанию `jira`;
|
`sdlc-hub agent publish [--module <модуль>] PATH` SHALL публиковать файл агента модуля (по умолчанию `jira`;
|
||||||
неизвестный модуль или модуль без команд — код 2 и список модулей с агентами в stderr): текст в UTF-8 не больше 1 МБ,
|
неизвестный модуль или модуль без команд — код 2 и список модулей с агентами в stderr): текст в UTF-8 не больше 1 МБ,
|
||||||
ровно одна строка `AGENT_VERSION = <целое>`, версия больше опубликованной для этого модуля. Файл SHALL записываться в
|
ровно одна строка `AGENT_VERSION = <целое>`, версия больше опубликованной для этого модуля. Файл SHALL записываться в
|
||||||
`<JIRA_HUB_HOME>/release/<модуль>/agent.py` (каталоги 0700), затем `manifest.json` `{version, sha256, size,
|
`<SDLC_HUB_HOME>/release/<модуль>/agent.py` (каталоги 0700), затем `manifest.json` `{version, sha256, size,
|
||||||
published_at}`; оба — через временный файл с `fsync` и переименование, манифест последним. Прежний `agent.py` на время
|
published_at}`; оба — через временный файл с `fsync` и переименование, манифест последним. Прежний `agent.py` на время
|
||||||
публикации SHALL переименовываться в `agent.py.prev` и удаляться после записи манифеста; если запись нового файла или
|
публикации SHALL переименовываться в `agent.py.prev` и удаляться после записи манифеста; если запись нового файла или
|
||||||
манифеста не удалась (`OSError`), хаб SHALL вернуть `agent.py.prev` на место `agent.py`, удалить временные файлы и
|
манифеста не удалась (`OSError`), хаб SHALL вернуть `agent.py.prev` на место `agent.py`, удалить временные файлы и
|
||||||
@ -363,17 +363,17 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report
|
|||||||
(`rolled_back`), «v<to> отвергнута: <error>» (`rejected`), иначе `null`.
|
(`rolled_back`), «v<to> отвергнута: <error>» (`rejected`), иначе `null`.
|
||||||
|
|
||||||
#### Scenario: Публикация новой версии
|
#### Scenario: Публикация новой версии
|
||||||
- **WHEN** опубликована v5, `jira-hub agent publish jira-agent/src/agent.py` с `AGENT_VERSION = 6`
|
- **WHEN** опубликована v5, `sdlc-hub agent publish jira-agent/src/agent.py` с `AGENT_VERSION = 6`
|
||||||
- **THEN** в `release/jira/` — новый `agent.py` и манифест с `version = 6`, `agent.py.prev` нет; вывод «опубликована v6,
|
- **THEN** в `release/jira/` — новый `agent.py` и манифест с `version = 6`, `agent.py.prev` нет; вывод «опубликована v6,
|
||||||
sha256 …»; код 0
|
sha256 …»; код 0
|
||||||
|
|
||||||
#### Scenario: Публикация eva-agent
|
#### Scenario: Публикация eva-agent
|
||||||
- **WHEN** `jira-hub agent publish --module wiki eva-agent/src/agent.py` с `AGENT_VERSION = 1`, опубликована jira v14
|
- **WHEN** `sdlc-hub agent publish --module wiki eva-agent/src/agent.py` с `AGENT_VERSION = 1`, опубликована jira v14
|
||||||
- **THEN** в `release/wiki/` — v1; `claim` агента `wiki` получает `agent_release {version: 1, …}`, `claim` агента `jira` —
|
- **THEN** в `release/wiki/` — v1; `claim` агента `wiki` получает `agent_release {version: 1, …}`, `claim` агента `jira` —
|
||||||
`{version: 14, …}`
|
`{version: 14, …}`
|
||||||
|
|
||||||
#### Scenario: Неизвестный модуль
|
#### Scenario: Неизвестный модуль
|
||||||
- **WHEN** `jira-hub agent publish --module nope agent.py`
|
- **WHEN** `sdlc-hub agent publish --module nope agent.py`
|
||||||
- **THEN** код 2, в stderr «неизвестный модуль nope» и список модулей с агентами; `release/` не изменён
|
- **THEN** код 2, в stderr «неизвестный модуль nope» и список модулей с агентами; `release/` не изменён
|
||||||
|
|
||||||
#### Scenario: Версия не выше опубликованной
|
#### Scenario: Версия не выше опубликованной
|
||||||
@ -407,7 +407,7 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report
|
|||||||
- **THEN** публикация v14 лежит в `release/jira/`, `claim` агента `jira` получает `agent_release {version: 14, …}`
|
- **THEN** публикация v14 лежит в `release/jira/`, `claim` агента `jira` получает `agent_release {version: 14, …}`
|
||||||
|
|
||||||
### Requirement: Раздача веб-приложения
|
### Requirement: Раздача веб-приложения
|
||||||
Хаб SHALL отдавать собранное веб-приложение (спека `web-app`) из каталога пакета `src/jira_hub/web/app/`, читая файлы
|
Хаб SHALL отдавать собранное веб-приложение (спека `web-app`) из каталога пакета `src/sdlc_hub/web/app/`, читая файлы
|
||||||
с диска на каждый запрос:
|
с диска на каждый запрос:
|
||||||
- `GET /app/assets/<путь>` — файл из `assets/` сборки; путь, который после разрешения выходит за `assets/`, не
|
- `GET /app/assets/<путь>` — файл из `assets/` сборки; путь, который после разрешения выходит за `assets/`, не
|
||||||
существует или содержит нулевой байт, — 404; `Content-Type` по расширению (`.js` — `text/javascript; charset=utf-8`, `.css` —
|
существует или содержит нулевой байт, — 404; `Content-Type` по расширению (`.js` — `text/javascript; charset=utf-8`, `.css` —
|
||||||
@ -416,7 +416,7 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report
|
|||||||
`Cache-Control: public, max-age=31536000, immutable`;
|
`Cache-Control: public, max-age=31536000, immutable`;
|
||||||
- любой другой `GET /app` или `GET /app/…` — `index.html` сборки, `text/html; charset=utf-8`, `Cache-Control: no-cache`;
|
- любой другой `GET /app` или `GET /app/…` — `index.html` сборки, `text/html; charset=utf-8`, `Cache-Control: no-cache`;
|
||||||
- если `index.html` сборки нет — на любой `GET /app…` 503 `text/html; charset=utf-8` с текстом, что приложение не
|
- если `index.html` сборки нет — на любой `GET /app…` 503 `text/html; charset=utf-8` с текстом, что приложение не
|
||||||
собрано и нужно выполнить `make web` в каталоге jira-hub;
|
собрано и нужно выполнить `make web` в каталоге sdlc-hub;
|
||||||
- `GET /` и `GET /links` — 302 с `Location: /app/board`.
|
- `GET /` и `GET /links` — 302 с `Location: /app/board`.
|
||||||
Сборка приложения не хранится в git; её создаёт `make web`.
|
Сборка приложения не хранится в git; её создаёт `make web`.
|
||||||
|
|
||||||
@ -665,3 +665,52 @@ from_string, to, to_string}`. До записи хаб SHALL проверить
|
|||||||
#### Scenario: Два вложения с одним именем
|
#### Scenario: Два вложения с одним именем
|
||||||
- **WHEN** у задачи два вложения «скрин.png» (#10 от 14.10, #20 от 20.10); комментарий — «!скрин.png!»
|
- **WHEN** у задачи два вложения «скрин.png» (#10 от 14.10, #20 от 20.10); комментарий — «!скрин.png!»
|
||||||
- **THEN** у комментария `attachment_ids = ["20"]`
|
- **THEN** у комментария `attachment_ids = ["20"]`
|
||||||
|
|
||||||
|
### Requirement: Каталог данных прежнего имени
|
||||||
|
Если `SDLC_HUB_HOME` не задан, `~/.local/share/sdlc-hub/` не существует, а `~/.local/share/jira-hub/` существует, любая
|
||||||
|
команда `sdlc-hub`, которой нужен каталог данных (`serve`, `mcp`, `tokens`, `init`, `import`, `agent publish`), SHALL
|
||||||
|
завершиться с кодом 2 и текстом в stderr: каталог данных лежит под прежним именем, перенести его —
|
||||||
|
`scripts/cutover.sh` (или `mv ~/.local/share/jira-hub ~/.local/share/sdlc-hub` при остановленном хабе). Команда MUST NOT
|
||||||
|
создавать новый каталог, переносить или менять прежний. Если существуют оба каталога, используется новый, в stderr —
|
||||||
|
предупреждение, что прежний можно удалить после проверки.
|
||||||
|
|
||||||
|
#### Scenario: Код обновлён, каталог не перенесён
|
||||||
|
- **WHEN** есть только `~/.local/share/jira-hub/`, запущен `sdlc-hub serve`
|
||||||
|
- **THEN** код выхода 2, в stderr — подсказка о переносе; `~/.local/share/sdlc-hub/` не создан, прежний каталог не тронут
|
||||||
|
|
||||||
|
#### Scenario: Задан SDLC_HUB_HOME
|
||||||
|
- **WHEN** `SDLC_HUB_HOME=/tmp/x`, есть `~/.local/share/jira-hub/`
|
||||||
|
- **THEN** команда работает с `/tmp/x`, проверка прежнего каталога не выполняется
|
||||||
|
|
||||||
|
### Requirement: Переключение живого хаба
|
||||||
|
`scripts/cutover.sh` SHALL переводить живую установку Mac mini со старого имени на новое. Без `--apply` скрипт SHALL
|
||||||
|
только печатать шаги и найденные проблемы, ничего не меняя. С `--apply` — по порядку, с остановкой на первой ошибке:
|
||||||
|
1. проверки: основной checkout `jira-bridge/jira-hub` чистый, на `main` и совпадает с `origin/main` (владелец сделал
|
||||||
|
`git pull --ff-only` прямо перед запуском: скрипт приходит этим pull; неотслеживаемая прежняя сборка `src/jira_hub/`
|
||||||
|
без tracked-файлов не считается грязью); в Gitea доступен `aep/sdlc-hub` (владелец переименовал репозиторий; адрес
|
||||||
|
выводится из `origin` заменой `/aep/jira-hub` на `/aep/sdlc-hub`, протокол сохраняется, `CUTOVER_REMOTE`
|
||||||
|
переопределяет); есть `uv`, `make`, `node`, `npm`, `claude`, `launchctl`; `~/.local/share/sdlc-hub/` и `jira-bridge/sdlc-hub` не существуют;
|
||||||
|
2. остановить launchd `ru.aepif.jira-hub`, дождаться закрытия порта и переименовать его plist в `*.pre-sdlc`;
|
||||||
|
3. перенести каталог данных `~/.local/share/jira-hub` → `~/.local/share/sdlc-hub`;
|
||||||
|
4. перенести checkout `jira-bridge/jira-hub` → `jira-bridge/sdlc-hub`, удалить неотслеживаемый `src/jira_hub`,
|
||||||
|
`git worktree repair`, `origin` → `aep/sdlc-hub`, пересоздать `.venv` (`uv sync`), `make web`;
|
||||||
|
5. поставить и запустить launchd `ru.aepif.sdlc-hub` (`uv run --project <checkout> sdlc-hub serve`, лог
|
||||||
|
`~/Library/Logs/sdlc-hub.log`) и дождаться ответа хаба на порту из настроек;
|
||||||
|
6. в проектах-потребителях (по умолчанию `~/projects/awp-ca`, `~/projects/stocks_drrk` и каждый
|
||||||
|
`~/projects/*/.mcp.json`, где есть `jira-hub`, например `sfm_foxpro`; сухой прогон печатает полный список): в `.mcp.json` заменить записи
|
||||||
|
`jira-hub` → `sdlc-jira` (`sdlc-hub mcp jira`) и `jira-hub-wiki` → `sdlc-wiki` (`sdlc-hub mcp wiki`) с новым путём; в
|
||||||
|
`.claude/settings*.json` — имена серверов (`enabledMcpjsonServers` и т. п.) и разрешения `mcp__jira-hub__` →
|
||||||
|
`mcp__sdlc-jira__`, `mcp__jira-hub-wiki__` → `mcp__sdlc-wiki__`; в `CLAUDE.md` и `.claude/skills/**` — те же имена
|
||||||
|
серверов и инструментов. Прочие файлы проектов (заметки, отчёты) не трогаются. Перед правкой файл копируется рядом
|
||||||
|
с суффиксом `.pre-sdlc`; коммиты скрипт не делает;
|
||||||
|
7. в user scope Claude Code заменить `jira-hub` на `sdlc-jira` (`claude mcp remove`/`add`).
|
||||||
|
Каждый выполненный шаг SHALL печататься; в конце — сводка, что осталось владельцу (`/mcp` reconnect в открытых сессиях,
|
||||||
|
`codegraph init` в checkout и worktree, удалить копии `.pre-sdlc` после проверки). Шаги откатываются в обратном порядке по инструкции из README.
|
||||||
|
|
||||||
|
#### Scenario: Сухой прогон
|
||||||
|
- **WHEN** `bash scripts/cutover.sh` без аргументов
|
||||||
|
- **THEN** напечатаны шаги и проверки, файловая система, launchd и конфигурации Claude не изменены
|
||||||
|
|
||||||
|
#### Scenario: Репозиторий в Gitea не переименован
|
||||||
|
- **WHEN** `bash scripts/cutover.sh --apply`, `aep/sdlc-hub` в Gitea не найден
|
||||||
|
- **THEN** скрипт останавливается на шаге 1 с подсказкой, launchd и каталоги не тронуты
|
||||||
|
|||||||
@ -1,30 +1,35 @@
|
|||||||
# mcp Specification
|
# mcp Specification
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
MCP-сервер хаба (`jira-hub mcp`, stdio) для Claude: инструменты чтения зеркала и заметок, постановки и отслеживания пакетов команд, обновления зеркала, статуса агента и настроек — поверх HTTP API хаба с клиентским токеном.
|
MCP-серверы модулей хаба (`sdlc-hub mcp <модуль>`, stdio) для Claude: инструменты чтения зеркала и заметок, постановки и отслеживания пакетов команд, обновления зеркала, статуса агента и настроек — поверх HTTP API хаба с клиентским токеном.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Сервер и регистрация
|
### Requirement: Сервер и регистрация
|
||||||
`jira-hub mcp [<модуль>] [--project <метка>]` SHALL запускать MCP-сервер модуля хаба по stdio (пакет `mcp`), ходящий в
|
`sdlc-hub mcp <модуль> [--project <метка>]` SHALL запускать MCP-сервер модуля хаба по stdio (пакет `mcp`), ходящий в
|
||||||
HTTP API хаба клиентским токеном из настроек. Модуль по умолчанию — `jira`: `jira-hub mcp [--project <метка>]` и
|
HTTP API хаба клиентским токеном из настроек; сервер называется `sdlc-<модуль>` (`sdlc-jira`, `sdlc-wiki`). Модуль
|
||||||
`jira-hub mcp jira [--project <метка>]` SHALL поднимать один и тот же сервер с именем `jira-hub` и одним и тем же набором
|
обязателен: без него — код выхода 2 и список модулей в stderr, сервер не запускается; неизвестный модуль — так же.
|
||||||
инструментов. Неизвестный модуль — код выхода 2 и список модулей в stderr, сервер не запускается. Общая регистрация — в
|
Общая регистрация — в user scope Claude Code как `sdlc-jira` (`uv run --project <путь> sdlc-hub mcp jira`), без
|
||||||
user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub mcp`), без проекта. Репозиторий проекта SHALL
|
проекта. Репозиторий проекта SHALL регистрировать серверы модулей в своём `.mcp.json` (project scope) с
|
||||||
регистрировать `jira-hub` в своём `.mcp.json` (project scope) с `--project <метка>`; эта запись перекрывает общую. Если хаб
|
`--project <метка>`; эта запись перекрывает общую. Если хаб недоступен, каждый инструмент SHALL вернуть текст «хаб не
|
||||||
недоступен, каждый инструмент SHALL вернуть текст «хаб не запущен: jira-hub serve», а не исключение.
|
запущен: sdlc-hub serve», а не исключение.
|
||||||
|
|
||||||
#### Scenario: Хаб выключен
|
#### Scenario: Хаб выключен
|
||||||
- **WHEN** вызван `issues` при остановленном хабе
|
- **WHEN** вызван `issues` при остановленном хабе
|
||||||
- **THEN** ответ инструмента — одна строка с подсказкой, сервер MCP продолжает работать
|
- **THEN** ответ инструмента — одна строка с подсказкой, сервер MCP продолжает работать
|
||||||
|
|
||||||
|
#### Scenario: Сервер модуля jira
|
||||||
|
- **WHEN** запущен `sdlc-hub mcp jira --project AWPCA`
|
||||||
|
- **THEN** сервер называется `sdlc-jira`, список инструментов и их описания совпадают с прежним сервером `jira-hub`,
|
||||||
|
сессия привязана к `AWPCA`
|
||||||
|
|
||||||
#### Scenario: Модуль по умолчанию
|
#### Scenario: Модуль по умолчанию
|
||||||
- **WHEN** запущены `jira-hub mcp --project AWPCA` и `jira-hub mcp jira --project AWPCA`
|
- **WHEN** запущен `sdlc-hub mcp --project AWPCA`
|
||||||
- **THEN** оба сервера называются `jira-hub`, список инструментов и их описания совпадают, сессия привязана к `AWPCA`
|
- **THEN** модуля по умолчанию больше нет: код выхода 2, в stderr — подсказка указать модуль и список модулей (`jira`, `wiki`)
|
||||||
|
|
||||||
#### Scenario: Неизвестный модуль
|
#### Scenario: Неизвестный модуль
|
||||||
- **WHEN** запущен `jira-hub mcp nope`
|
- **WHEN** запущен `sdlc-hub mcp nope`
|
||||||
- **THEN** код выхода 2, в stderr — «неизвестный модуль nope» и список модулей (`jira`)
|
- **THEN** код выхода 2, в stderr — «неизвестный модуль nope» и список модулей (`jira`, `wiki`)
|
||||||
|
|
||||||
### Requirement: Инструменты
|
### Requirement: Инструменты
|
||||||
| Инструмент | Параметры | Результат |
|
| Инструмент | Параметры | Результат |
|
||||||
@ -61,7 +66,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
других проектов запрещены, а команды над задачами вне проекта отклоняются; `board_id` для `ensure_sprint` выставляет хаб.
|
других проектов запрещены, а команды над задачами вне проекта отклоняются; `board_id` для `ensure_sprint` выставляет хаб.
|
||||||
|
|
||||||
Ответ `project_create` SHALL содержать фрагмент `.mcp.json`
|
Ответ `project_create` SHALL содержать фрагмент `.mcp.json`
|
||||||
`{"mcpServers": {"jira-hub": {"command": "uv", "args": ["run", "--project", "<корень хаба>", "jira-hub", "mcp",
|
`{"mcpServers": {"sdlc-jira": {"command": "uv", "args": ["run", "--project", "<корень хаба>", "sdlc-hub", "mcp", "jira",
|
||||||
"--project", "<метка>"]}}}`, где корень хаба — корень репозитория, из которого загружен пакет текущего процесса MCP
|
"--project", "<метка>"]}}}`, где корень хаба — корень репозитория, из которого загружен пакет текущего процесса MCP
|
||||||
(вычисляется от расположения пакета, без зашитого пути), и указание: положить файл в корень репозитория проекта (слить
|
(вычисляется от расположения пакета, без зашитого пути), и указание: положить файл в корень репозитория проекта (слить
|
||||||
с имеющимся `.mcp.json`) и переподключить MCP (`/mcp` → reconnect, одобрить сервер проекта).
|
с имеющимся `.mcp.json`) и переподключить MCP (`/mcp` → reconnect, одобрить сервер проекта).
|
||||||
@ -121,7 +126,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
- **THEN** ответ «у AURORA-9001 нет комментария #999»
|
- **THEN** ответ «у AURORA-9001 нет комментария #999»
|
||||||
|
|
||||||
### Requirement: Привязка сессии к проекту
|
### Requirement: Привязка сессии к проекту
|
||||||
`jira-hub mcp --project <метка>` SHALL привязывать сервер MCP к проекту. Привязка SHALL проверяться при каждом вызове
|
`sdlc-hub mcp <модуль> --project <метка>` SHALL привязывать сервер MCP к проекту. Привязка SHALL проверяться при каждом вызове
|
||||||
инструмента по `GET /projects`: метки нет — ответ «проект <метка> не заведён в хабе: project_create или исправьте
|
инструмента по `GET /projects`: метки нет — ответ «проект <метка> не заведён в хабе: project_create или исправьте
|
||||||
--project в .mcp.json» (кроме `projects`, `project_create`).
|
--project в .mcp.json» (кроме `projects`, `project_create`).
|
||||||
|
|
||||||
@ -226,7 +231,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
- **WHEN** `comment {key: "AURORA-1650", comment_id: "101"}` показал «вложения: #31337 скрин 1.png (234,4 КБ)»; вызван
|
- **WHEN** `comment {key: "AURORA-1650", comment_id: "101"}` показал «вложения: #31337 скрин 1.png (234,4 КБ)»; вызван
|
||||||
`attachment {key: "AURORA-1650", attachment: "#31337"}`, агент онлайн, файла у хаба нет
|
`attachment {key: "AURORA-1650", attachment: "#31337"}`, агент онлайн, файла у хаба нет
|
||||||
- **THEN** поставлена `fetch_attachment`; через несколько секунд ответ — путь
|
- **THEN** поставлена `fetch_attachment`; через несколько секунд ответ — путь
|
||||||
`…/jira-hub/downloads/31337/скрин 1.png` и строка «скрин 1.png (234,4 КБ), image/png, задача AURORA-1650»; файл по пути
|
`…/sdlc-hub/downloads/31337/скрин 1.png` и строка «скрин 1.png (234,4 КБ), image/png, задача AURORA-1650»; файл по пути
|
||||||
читается
|
читается
|
||||||
|
|
||||||
#### Scenario: Повторный вызов
|
#### Scenario: Повторный вызов
|
||||||
@ -249,14 +254,14 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
комментария 101 строка «файлы: #31337 скрин 1.png»
|
комментария 101 строка «файлы: #31337 скрин 1.png»
|
||||||
|
|
||||||
### Requirement: MCP вики
|
### Requirement: MCP вики
|
||||||
`jira-hub mcp wiki [--project <метка>]` SHALL запускать по stdio MCP-сервер модуля `wiki` с именем `jira-hub-wiki`,
|
`sdlc-hub mcp wiki [--project <метка>]` SHALL запускать по stdio MCP-сервер модуля `wiki` с именем `sdlc-wiki`,
|
||||||
ходящий в HTTP API хаба клиентским токеном; хаб недоступен — каждый инструмент отвечает «хаб не запущен: jira-hub serve».
|
ходящий в HTTP API хаба клиентским токеном; хаб недоступен — каждый инструмент отвечает «хаб не запущен: sdlc-hub serve».
|
||||||
Привязка к проекту проверяется так же, как у сервера jira («Привязка сессии к проекту»). Инструменты:
|
Привязка к проекту проверяется так же, как у сервера jira («Привязка сессии к проекту»). Инструменты:
|
||||||
|
|
||||||
| Инструмент | Параметры | Ответ |
|
| Инструмент | Параметры | Ответ |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `projects` | — | таблица: метка, название, корень вики, страниц, последняя синхронизация; текущий проект отмечен |
|
| `projects` | — | таблица: метка, название, корень вики, страниц, последняя синхронизация; текущий проект отмечен |
|
||||||
| `project_create` | `label`, `name`, `wiki_root` (необязательно) | `POST /projects` с автором `claude`; проект и фрагмент `.mcp.json` с записью `jira-hub-wiki` |
|
| `project_create` | `label`, `name`, `wiki_root` (необязательно) | `POST /projects` с автором `claude`; проект и фрагмент `.mcp.json` с записью `sdlc-wiki` |
|
||||||
| `root_set` | `root` | `PATCH /projects/<текущий> {wiki_root}`; новый корень и id пакета синхронизации |
|
| `root_set` | `root` | `PATCH /projects/<текущий> {wiki_root}`; новый корень и id пакета синхронизации |
|
||||||
| `tree` | `code` (необязательно), `depth` (по умолчанию 2) | дерево от корня или от `code`: отступ, код, название, размер |
|
| `tree` | `code` (необязательно), `depth` (по умолчанию 2) | дерево от корня или от `code`: отступ, код, название, размер |
|
||||||
| `page` | `code` или `title`, `offset` (по умолчанию 0) | шапка (код, путь в дереве, адрес в Eva, дата синхронизации, вложения с `id`) и Markdown с `offset` до 20 000 символов, в конце — «продолжение: offset=<N>», если текст длиннее; `title` ищется точно, иначе — до 10 кандидатов с кодами |
|
| `page` | `code` или `title`, `offset` (по умолчанию 0) | шапка (код, путь в дереве, адрес в Eva, дата синхронизации, вложения с `id`) и Markdown с `offset` до 20 000 символов, в конце — «продолжение: offset=<N>», если текст длиннее; `title` ищется точно, иначе — до 10 кандидатов с кодами |
|
||||||
@ -267,7 +272,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
|
|
||||||
Инструменты, кроме `projects` и `project_create`, SHALL требовать привязки к проекту. В привязанной сессии без корня
|
Инструменты, кроме `projects` и `project_create`, SHALL требовать привязки к проекту. В привязанной сессии без корня
|
||||||
(кроме `root_set`, `status`) ответ SHALL быть «у проекта <метка> нет корня вики: root_set DOC-…»; с корнем, но без
|
(кроме `root_set`, `status`) ответ SHALL быть «у проекта <метка> нет корня вики: root_set DOC-…»; с корнем, но без
|
||||||
синхронизаций — «вики проекта ещё не синхронизирована: sync». Набор инструментов и их описания у сервера `jira-hub`
|
синхронизаций — «вики проекта ещё не синхронизирована: sync». Набор инструментов и их описания у сервера `sdlc-jira`
|
||||||
(модуль jira) SHALL остаться прежним.
|
(модуль jira) SHALL остаться прежним.
|
||||||
|
|
||||||
#### Scenario: Поиск из сессии awp-ca
|
#### Scenario: Поиск из сессии awp-ca
|
||||||
@ -284,5 +289,5 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub
|
|||||||
- **THEN** ответ «у проекта sfm_kedr нет корня вики: root_set DOC-…», запроса `GET /wiki/tree` нет
|
- **THEN** ответ «у проекта sfm_kedr нет корня вики: root_set DOC-…», запроса `GET /wiki/tree` нет
|
||||||
|
|
||||||
#### Scenario: Сервер jira не изменился
|
#### Scenario: Сервер jira не изменился
|
||||||
- **WHEN** запущен `jira-hub mcp --project AWPCA`
|
- **WHEN** запущен `sdlc-hub mcp jira --project AWPCA`
|
||||||
- **THEN** имя сервера `jira-hub`, список инструментов и описания совпадают с эталоном до change
|
- **THEN** имя сервера `sdlc-jira`, список инструментов и описания совпадают с эталоном до change
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Инициализация настроек
|
### Requirement: Инициализация настроек
|
||||||
`jira-hub init --config <admin/jira-sync/config.example.json>` SHALL перенести в `setting`: `project_key`, типы задач,
|
`sdlc-hub init --config <admin/jira-sync/config.example.json>` SHALL перенести в `setting`: `project_key`, типы задач,
|
||||||
`epic_name_field_id`, `fields`, `epic_link`, `priority_map`, `labels`, `status_labels`, `kind_labels`, `comment_marker`,
|
`epic_name_field_id`, `fields`, `epic_link`, `priority_map`, `labels`, `status_labels`, `kind_labels`, `comment_marker`,
|
||||||
`pacing` (в новую форму по типам команд), `transitions.map`, `link_types`; сгенерировать два токена; завести проект
|
`pacing` (в новую форму по типам команд), `transitions.map`, `link_types`; сгенерировать два токена; завести проект
|
||||||
`AWPCA` «АРМ КА» с `board_id` из `sprints.board_id` конфига (если проекта `AWPCA` ещё нет). Поля `auth`, `base_url`,
|
`AWPCA` «АРМ КА» с `board_id` из `sprints.board_id` конфига (если проекта `AWPCA` ещё нет). Поля `auth`, `base_url`,
|
||||||
@ -21,7 +21,7 @@
|
|||||||
- **THEN** есть проект `AWPCA` «АРМ КА» с `board_id = 4216`; в `setting` нет `base_jql` и `board_id`
|
- **THEN** есть проект `AWPCA` «АРМ КА» с `board_id = 4216`; в `setting` нет `base_jql` и `board_id`
|
||||||
|
|
||||||
### Requirement: Импорт CSV-пакета
|
### Requirement: Импорт CSV-пакета
|
||||||
`jira-hub import <папка admin/jira-sync>` SHALL прочитать `tasks.csv`, `new_tasks.csv`, `epics.csv` и заполнить
|
`sdlc-hub import <папка admin/jira-sync>` SHALL прочитать `tasks.csv`, `new_tasks.csv`, `epics.csv` и заполнить
|
||||||
`issue_note` по ключу Jira: статус по факту, вид работы, что сделано, что осталось, внутренние колонки. Строки
|
`issue_note` по ключу Jira: статус по факту, вид работы, что сделано, что осталось, внутренние колонки. Строки
|
||||||
`new_tasks.csv` без ключа SHALL получать временный ключ `new:<номер строки>`; строки `tasks.csv` и `epics.csv` без ключа
|
`new_tasks.csv` без ключа SHALL получать временный ключ `new:<номер строки>`; строки `tasks.csv` и `epics.csv` без ключа
|
||||||
SHALL пропускаться. `links.csv` SHALL импортироваться как сверочный набор в отчёт импорта (ожидаемые связи против зеркала
|
SHALL пропускаться. `links.csv` SHALL импортироваться как сверочный набор в отчёт импорта (ожидаемые связи против зеркала
|
||||||
|
|||||||
@ -1,20 +1,24 @@
|
|||||||
# web-app Specification
|
# web-app Specification
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
Веб-приложение хаба (`web/`, React + TypeScript + Vite) под префиксом `/app/`: вход по клиентскому токену, доска связей (перенос `links.html`) и страницы задач со служебными заметками; сборка в `src/jira_hub/web/app/`.
|
Веб-приложение хаба (`web/`, React + TypeScript + Vite) под префиксом `/app/`: вход по клиентскому токену, доска связей (перенос `links.html`) и страницы задач со служебными заметками; сборка в `src/sdlc_hub/web/app/`.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Requirement: Вход по токену
|
### Requirement: Вход по токену
|
||||||
Приложение SHALL работать с API хаба того же хоста под клиентским токеном: хранить его в `localStorage` под ключом
|
Приложение SHALL работать с API хаба того же хоста под клиентским токеном: хранить его в `localStorage` под ключом
|
||||||
`jira-hub-token` (и в памяти, если хранилище закрыто) и передавать в заголовке `Authorization: Bearer` каждого запроса.
|
`sdlc-hub-token` (и в памяти, если хранилище закрыто) и передавать в заголовке `Authorization: Bearer` каждого запроса.
|
||||||
Если токена нет или любой запрос ответил 401, приложение SHALL показывать экран входа вместо страниц: поле токена с
|
Если токена нет или любой запрос ответил 401, приложение SHALL показывать экран входа вместо страниц: поле токена с
|
||||||
подсказкой `uv run jira-hub tokens`, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода.
|
подсказкой `uv run sdlc-hub tokens`, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода.
|
||||||
Введённый токен SHALL проверяться запросом `GET /settings` и сохраняться только при успехе. Действие «Сменить токен»
|
Введённый токен SHALL проверяться запросом `GET /settings` и сохраняться только при успехе. Действие «Сменить токен»
|
||||||
SHALL очищать токен и показывать экран входа. Ответ 401 SHALL выводить на экран входа, только если запрос ушёл с текущим токеном: запоздавший 401 на
|
SHALL очищать токен и показывать экран входа. Ответ 401 SHALL выводить на экран входа, только если запрос ушёл с текущим токеном: запоздавший 401 на
|
||||||
запрос с прежним токеном после входа с новым не учитывается. Если проверка токена получила ответ 4xx, кроме 401,
|
запрос с прежним токеном после входа с новым не учитывается. Если проверка токена получила ответ 4xx, кроме 401,
|
||||||
экран входа SHALL показывать текст ошибки хаба, а не «хаб не отвечает».
|
экран входа SHALL показывать текст ошибки хаба, а не «хаб не отвечает».
|
||||||
|
|
||||||
|
Ключи `localStorage` приложения с префиксом `sdlc-hub-` (`sdlc-hub-token`, `sdlc-hub-sidebar`, `sdlc-hub-legend`) SHALL
|
||||||
|
при первом чтении, если ключа нет, брать значение одноимённого ключа с префиксом `jira-hub-`, записывать его под новым
|
||||||
|
ключом и удалять прежний.
|
||||||
|
|
||||||
#### Scenario: Первый вход
|
#### Scenario: Первый вход
|
||||||
- **WHEN** приложение открыто впервые, токена нет
|
- **WHEN** приложение открыто впервые, токена нет
|
||||||
- **THEN** показан экран входа; после ввода верного токена открывается запрошенная страница, токен сохранён
|
- **THEN** показан экран входа; после ввода верного токена открывается запрошенная страница, токен сохранён
|
||||||
@ -24,8 +28,8 @@ SHALL очищать токен и показывать экран входа.
|
|||||||
- **THEN** показан экран входа с пояснением, что токен не подошёл; панели доски и задач не видны
|
- **THEN** показан экран входа с пояснением, что токен не подошёл; панели доски и задач не видны
|
||||||
|
|
||||||
#### Scenario: Токен от прежней доски
|
#### Scenario: Токен от прежней доски
|
||||||
- **WHEN** в `localStorage` есть `jira-hub-token`, сохранённый страницей `links.html`
|
- **WHEN** в `localStorage` нет `sdlc-hub-token`, но есть `jira-hub-token` (сохранён до переименования хаба)
|
||||||
- **THEN** приложение входит с ним без запроса токена
|
- **THEN** приложение входит с ним без запроса токена; значение перенесено в `sdlc-hub-token`, `jira-hub-token` удалён
|
||||||
|
|
||||||
#### Scenario: Запоздавший 401 от прежнего токена
|
#### Scenario: Запоздавший 401 от прежнего токена
|
||||||
- **WHEN** запрос ушёл с прежним токеном, пользователь вошёл с новым, и после этого запрос ответил 401
|
- **WHEN** запрос ушёл с прежним токеном, пользователь вошёл с новым, и после этого запрос ответил 401
|
||||||
@ -40,13 +44,13 @@ SHALL очищать токен и показывать экран входа.
|
|||||||
задач), `/app/feed` (лента изменений, требование «Лента изменений») и `/app/issues/{key}` (карточка задачи — окно поверх страницы, требование «Карточка задачи»); `/app` и `/app/` SHALL вести на `/app/board`, неизвестный путь —
|
задач), `/app/feed` (лента изменений, требование «Лента изменений») и `/app/issues/{key}` (карточка задачи — окно поверх страницы, требование «Карточка задачи»); `/app` и `/app/` SHALL вести на `/app/board`, неизвестный путь —
|
||||||
на страницу «не найдено» со ссылками на разделы. Боковая панель SHALL быть закреплена у левого края окна на всю его
|
на страницу «не найдено» со ссылками на разделы. Боковая панель SHALL быть закреплена у левого края окна на всю его
|
||||||
высоту и MUST NOT прокручиваться вместе со страницей; страница занимает остальную ширину. Сверху вниз панель SHALL
|
высоту и MUST NOT прокручиваться вместе со страницей; страница занимает остальную ширину. Сверху вниз панель SHALL
|
||||||
содержать: название «Хаб Jira» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий
|
содержать: название «SDLC Hub» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий
|
||||||
отмечен, в конце «+ Новый проект…»; требование «Проект в приложении»); разделы «Доска связей», «Задачи» и «Лента» — вертикальным
|
отмечен, в конце «+ Новый проект…»; требование «Проект в приложении»); разделы «Доска связей», «Задачи» и «Лента» — вертикальным
|
||||||
списком ссылок со значками (текущий отмечен `aria-current="page"`); внизу — блок данных Jira с действием «Обновить из
|
списком ссылок со значками (текущий отмечен `aria-current="page"`); внизу — блок данных Jira с действием «Обновить из
|
||||||
Jira» (требование «Обновление из Jira»), переключатель темы (требование «Тема») и меню с действием «Сменить токен».
|
Jira» (требование «Обновление из Jira»), переключатель темы (требование «Тема») и меню с действием «Сменить токен».
|
||||||
Свёрнутая панель SHALL быть узкой полосой с теми же действиями значками: «Развернуть панель», метка текущего проекта,
|
Свёрнутая панель SHALL быть узкой полосой с теми же действиями значками: «Развернуть панель», метка текущего проекта,
|
||||||
разделы, «Обновить из Jira» с точкой состояния агента, тема, меню; названия — в подсказках и `aria-label`. Свёртка
|
разделы, «Обновить из Jira» с точкой состояния агента, тема, меню; названия — в подсказках и `aria-label`. Свёртка
|
||||||
SHALL храниться в `localStorage` под ключом `jira-hub-sidebar` (`collapsed`; без записи — развёрнута). На экранах уже
|
SHALL храниться в `localStorage` под ключом `sdlc-hub-sidebar` (`collapsed`; без записи — развёрнута). На экранах уже
|
||||||
640 px панели в потоке нет: сверху SHALL быть закреплена строка с кнопкой «Меню», названием и кнопкой «Обновить из
|
640 px панели в потоке нет: сверху SHALL быть закреплена строка с кнопкой «Меню», названием и кнопкой «Обновить из
|
||||||
Jira»; «Меню» открывает развёрнутую панель поверх страницы, панель закрывается выбором раздела, клавишей Escape или
|
Jira»; «Меню» открывает развёрнутую панель поверх страницы, панель закрывается выбором раздела, клавишей Escape или
|
||||||
щелчком мимо неё. Горизонтальной прокрутки страницы на любой ширине нет.
|
щелчком мимо неё. Горизонтальной прокрутки страницы на любой ширине нет.
|
||||||
@ -116,7 +120,7 @@ Jira»; «Меню» открывает развёрнутую панель по
|
|||||||
управления: мультиселекты «Спринты» и «Исполнители», переключатели группы «Показать» («Соседи по связям», «Только
|
управления: мультиселекты «Спринты» и «Исполнители», переключатели группы «Показать» («Соседи по связям», «Только
|
||||||
передачи», «Скрыть без связей») и группы «Режим» («Критический путь», «По датам»), каждый с `aria-pressed` и прежней
|
передачи», «Скрыть без связей») и группы «Режим» («Критический путь», «По датам»), каждый с `aria-pressed` и прежней
|
||||||
подсказкой. Под панелью — легенда, которая сворачивается кнопкой «Легенда»; свёрнутая показывает число скрытых видов
|
подсказкой. Под панелью — легенда, которая сворачивается кнопкой «Легенда»; свёрнутая показывает число скрытых видов
|
||||||
работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `jira-hub-legend`. «Обновить из Jira»,
|
работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `sdlc-hub-legend`. «Обновить из Jira»,
|
||||||
тема и «Сменить токен» — в боковой панели приложения, на доске их нет.
|
тема и «Сменить токен» — в боковой панели приложения, на доске их нет.
|
||||||
|
|
||||||
У карточки задачи (не у заглушки) SHALL быть значок «Открыть карточку»,
|
У карточки задачи (не у заглушки) SHALL быть значок «Открыть карточку»,
|
||||||
@ -367,15 +371,15 @@ SHALL сообщать об этом с кнопкой «Закрыть»; ха
|
|||||||
### Requirement: Сборка и разработка
|
### Requirement: Сборка и разработка
|
||||||
Приложение SHALL быть npm-проектом в `web/` (React, TypeScript, Vite) с точными версиями зависимостей и
|
Приложение SHALL быть npm-проектом в `web/` (React, TypeScript, Vite) с точными версиями зависимостей и
|
||||||
`package-lock.json`. `make web` SHALL устанавливать зависимости (`npm ci`) и собирать приложение в
|
`package-lock.json`. `make web` SHALL устанавливать зависимости (`npm ci`) и собирать приложение в
|
||||||
`src/jira_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи
|
`src/sdlc_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи
|
||||||
задач и исходники), иконки и прочие ресурсы страницы SHALL входить в сборку: приложение не обращается к внешним
|
задач и исходники), иконки и прочие ресурсы страницы SHALL входить в сборку: приложение не обращается к внешним
|
||||||
адресам. `make check` SHALL запускать `uv run pytest`, проверку типов и линтер фронта и тесты фронта; зелёным считается
|
адресам. `make check` SHALL запускать `uv run pytest`, проверку типов и линтер фронта и тесты фронта; зелёным считается
|
||||||
только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `JIRA_HUB_URL` (по
|
только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `SDLC_HUB_URL` (по
|
||||||
умолчанию тестовый `http://127.0.0.1:8791`).
|
умолчанию тестовый `http://127.0.0.1:8791`).
|
||||||
|
|
||||||
#### Scenario: Сборка с нуля
|
#### Scenario: Сборка с нуля
|
||||||
- **WHEN** в чистом checkout выполнен `make web`
|
- **WHEN** в чистом checkout выполнен `make web`
|
||||||
- **THEN** в `src/jira_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки
|
- **THEN** в `src/sdlc_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки
|
||||||
|
|
||||||
#### Scenario: Без интернета
|
#### Scenario: Без интернета
|
||||||
- **WHEN** приложение открыто на машине без доступа в интернет
|
- **WHEN** приложение открыто на машине без доступа в интернет
|
||||||
|
|||||||
@ -81,7 +81,7 @@ links: [{ref, code}]}`, где `attachments` — ссылки и картинк
|
|||||||
- **THEN** 409 `sync_in_progress` с `batch_id` текущего пакета, нового пакета нет
|
- **THEN** 409 `sync_in_progress` с `batch_id` текущего пакета, нового пакета нет
|
||||||
|
|
||||||
### Requirement: Markdown-копия вики
|
### Requirement: Markdown-копия вики
|
||||||
Копия проекта SHALL лежать в `<JIRA_HUB_HOME>/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог
|
Копия проекта SHALL лежать в `<SDLC_HUB_HOME>/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог
|
||||||
`NN_<название>` внутри каталога родителя (`NN` — двузначная позиция среди соседей по `orderno`, затем по коду; у корня
|
`NN_<название>` внутри каталога родителя (`NN` — двузначная позиция среди соседей по `orderno`, затем по коду; у корня
|
||||||
номера нет; название без символов `<>:"/\|?*` и управляющих, обрезано до 80 символов; совпадение у соседей —
|
номера нет; название без символов `<>:"/\|?*` и управляющих, обрезано до 80 символов; совпадение у соседей —
|
||||||
суффикс `_2`, `_3`), в нём `index.md`: frontmatter `code`, `title`, `url`, `synced_at`, строка `# <title>` и текст. В
|
суффикс `_2`, `_3`), в нём `index.md`: frontmatter `code`, `title`, `url`, `synced_at`, строка `# <title>` и текст. В
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user