Change sdlc-rename: пакет, CLI, каталог данных, MCP sdlc-jira/sdlc-wiki, бренд и ключи хранилища веба, скрипт переключения живого хаба (#97). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
170 lines
20 KiB
Markdown
170 lines
20 KiB
Markdown
## MODIFIED Requirements
|
||
|
||
### Requirement: Вход по токену
|
||
Приложение SHALL работать с API хаба того же хоста под клиентским токеном: хранить его в `localStorage` под ключом
|
||
`sdlc-hub-token` (и в памяти, если хранилище закрыто) и передавать в заголовке `Authorization: Bearer` каждого запроса.
|
||
Если токена нет или любой запрос ответил 401, приложение SHALL показывать экран входа вместо страниц: поле токена с
|
||
подсказкой `uv run sdlc-hub tokens`, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода.
|
||
Введённый токен SHALL проверяться запросом `GET /settings` и сохраняться только при успехе. Действие «Сменить токен»
|
||
SHALL очищать токен и показывать экран входа. Ответ 401 SHALL выводить на экран входа, только если запрос ушёл с текущим токеном: запоздавший 401 на
|
||
запрос с прежним токеном после входа с новым не учитывается. Если проверка токена получила ответ 4xx, кроме 401,
|
||
экран входа SHALL показывать текст ошибки хаба, а не «хаб не отвечает».
|
||
|
||
Ключи `localStorage` приложения с префиксом `sdlc-hub-` (`sdlc-hub-token`, `sdlc-hub-sidebar`, `sdlc-hub-legend`) SHALL
|
||
при первом чтении, если ключа нет, брать значение одноимённого ключа с префиксом `jira-hub-`, записывать его под новым
|
||
ключом и удалять прежний.
|
||
|
||
#### Scenario: Первый вход
|
||
- **WHEN** приложение открыто впервые, токена нет
|
||
- **THEN** показан экран входа; после ввода верного токена открывается запрошенная страница, токен сохранён
|
||
|
||
#### Scenario: Устаревший токен
|
||
- **WHEN** сохранённый токен не подходит и запрос ответил 401
|
||
- **THEN** показан экран входа с пояснением, что токен не подошёл; панели доски и задач не видны
|
||
|
||
#### Scenario: Токен от прежней доски
|
||
- **WHEN** в `localStorage` нет `sdlc-hub-token`, но есть `jira-hub-token` (сохранён до переименования хаба)
|
||
- **THEN** приложение входит с ним без запроса токена; значение перенесено в `sdlc-hub-token`, `jira-hub-token` удалён
|
||
|
||
#### Scenario: Запоздавший 401 от прежнего токена
|
||
- **WHEN** запрос ушёл с прежним токеном, пользователь вошёл с новым, и после этого запрос ответил 401
|
||
- **THEN** приложение остаётся на странице, новый токен сохранён
|
||
|
||
#### Scenario: Хаб отверг проверку токена
|
||
- **WHEN** проверка `GET /settings` ответила 400 с `detail`
|
||
- **THEN** экран входа показывает текст `detail`, а не «хаб не отвечает»
|
||
|
||
### Requirement: Оболочка и адреса
|
||
Приложение SHALL открываться под префиксом `/app/` и иметь страницы `/app/board` (доска связей), `/app/issues` (список
|
||
задач), `/app/feed` (лента изменений, требование «Лента изменений») и `/app/issues/{key}` (карточка задачи — окно поверх страницы, требование «Карточка задачи»); `/app` и `/app/` SHALL вести на `/app/board`, неизвестный путь —
|
||
на страницу «не найдено» со ссылками на разделы. Боковая панель SHALL быть закреплена у левого края окна на всю его
|
||
высоту и MUST NOT прокручиваться вместе со страницей; страница занимает остальную ширину. Сверху вниз панель SHALL
|
||
содержать: название «SDLC Hub» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий
|
||
отмечен, в конце «+ Новый проект…»; требование «Проект в приложении»); разделы «Доска связей», «Задачи» и «Лента» — вертикальным
|
||
списком ссылок со значками (текущий отмечен `aria-current="page"`); внизу — блок данных Jira с действием «Обновить из
|
||
Jira» (требование «Обновление из Jira»), переключатель темы (требование «Тема») и меню с действием «Сменить токен».
|
||
Свёрнутая панель SHALL быть узкой полосой с теми же действиями значками: «Развернуть панель», метка текущего проекта,
|
||
разделы, «Обновить из Jira» с точкой состояния агента, тема, меню; названия — в подсказках и `aria-label`. Свёртка
|
||
SHALL храниться в `localStorage` под ключом `sdlc-hub-sidebar` (`collapsed`; без записи — развёрнута). На экранах уже
|
||
640 px панели в потоке нет: сверху SHALL быть закреплена строка с кнопкой «Меню», названием и кнопкой «Обновить из
|
||
Jira»; «Меню» открывает развёрнутую панель поверх страницы, панель закрывается выбором раздела, клавишей Escape или
|
||
щелчком мимо неё. Горизонтальной прокрутки страницы на любой ширине нет.
|
||
Неожиданная ошибка отрисовки страницы SHALL показываться сообщением с кнопкой «Перезагрузить», не пустым экраном.
|
||
|
||
#### Scenario: Переход между разделами
|
||
- **WHEN** на доске в боковой панели выбран раздел «Задачи»
|
||
- **THEN** адрес — `/app/issues` с текущим проектом, показан список задач; «назад» возвращает на доску
|
||
|
||
#### Scenario: Раздел «Лента»
|
||
- **WHEN** на доске в боковой панели выбран раздел «Лента»
|
||
- **THEN** адрес — `/app/feed` с текущим проектом, показана лента изменений за сегодня; раздел отмечен в панели
|
||
|
||
#### Scenario: Сменить токен
|
||
- **WHEN** в меню боковой панели выбрано «Сменить токен»
|
||
- **THEN** токен очищен, показан экран входа
|
||
|
||
#### Scenario: Панель на месте при прокрутке
|
||
- **WHEN** доска прокручена до самого низа
|
||
- **THEN** боковая панель видна целиком на прежнем месте: разделы и «Обновить из Jira» доступны без прокрутки вверх
|
||
|
||
#### Scenario: Свёрнутая панель
|
||
- **WHEN** нажата «Свернуть панель», страница перезагружена
|
||
- **THEN** панель свёрнута в полосу значков, доска шире; значок «Задачи» открывает список задач; «Развернуть панель»
|
||
возвращает полную панель
|
||
|
||
#### Scenario: Узкий экран
|
||
- **WHEN** ширина окна 390 px, нажата «Меню», в панели выбран раздел «Задачи»
|
||
- **THEN** панель открылась поверх страницы, после выбора закрыта, показан список задач; горизонтальной прокрутки нет
|
||
|
||
### Requirement: Доска связей
|
||
Страница `/app/board` SHALL воспроизводить поведение доски связей хаба (`links.html` ревизии 3931a88) без изменений,
|
||
кроме перечисленных в этом требовании и в требовании «Источник данных доски»: построение модели из `GET /board`, CSV
|
||
фильтра Jira и JSON доски (`arm-ka-links/1`); дорожки по эпикам со свёрткой и перестановкой, служебные дорожки внизу;
|
||
карточки с перетаскиванием, растяжкой, размером по содержимому и привязкой к сетке; стрелки связей с выравниванием;
|
||
фильтры по спринтам и исполнителям; легенда со скрытием видов работ и типов связей; «соседи по связям», «только
|
||
передачи», «скрыть без связей», «критический путь», «по датам» с колонками дат и их растяжкой; ящик «Проблемы»; таблица
|
||
связей; экспорт и импорт JSON; «Сбросить раскладку». Раскладка, дорожки и фильтры SHALL храниться в
|
||
`localStorage` под теми же ключами `graph-<имя>…`, что у `links.html`, с той же миграцией старых ключей.
|
||
Результат построения модели, видимой выборки, критического пути и проблем SHALL совпадать с результатом `links.html`
|
||
на одних и тех же данных и фильтрах.
|
||
|
||
Шапка доски (закреплена при прокрутке) SHALL содержать заголовок «Доска связей», счётчики (задачи из всех, соседи по
|
||
связям, связи из всех, скрытые без связей), пометку источника, если на доске снимок из файла (с действием вернуться к
|
||
данным хаба), событие доски, кнопку «Проблемы» со счётчиком и меню с действиями «Перечитать», «Загрузить файл…»,
|
||
«Экспорт JSON», «Таблица связей», «Привязка к сетке» и «Сбросить раскладку» (с подтверждением). Под шапкой — панель
|
||
управления: мультиселекты «Спринты» и «Исполнители», переключатели группы «Показать» («Соседи по связям», «Только
|
||
передачи», «Скрыть без связей») и группы «Режим» («Критический путь», «По датам»), каждый с `aria-pressed` и прежней
|
||
подсказкой. Под панелью — легенда, которая сворачивается кнопкой «Легенда»; свёрнутая показывает число скрытых видов
|
||
работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `sdlc-hub-legend`. «Обновить из Jira»,
|
||
тема и «Сменить токен» — в боковой панели приложения, на доске их нет.
|
||
|
||
У карточки задачи (не у заглушки) SHALL быть значок «Открыть карточку»,
|
||
открывающий `/app/issues/{key}` окном поверх доски; ключ карточки по-прежнему открывает задачу в Jira. Пока окно открыто
|
||
и после его закрытия доска SHALL сохранять прокрутку, раскладку, фильтры и строку состояния. Ещё два отличия от `links.html` (R-10, R-18): двойной щелчок по уголку
|
||
карточки меряет высоту по содержимому при новой ширине, а не при прежней; указатель при перетаскивании и растяжке
|
||
захватывается на нажатом элементе, а не на карточке, поэтому двойной щелчок по уголку сбрасывает размер. Загрузка файла CSV/JSON доступна только после входа, с экрана входа её нет.
|
||
Карточка задачи, статус Jira которой входит в `closed_statuses`, SHALL быть приглушена (полупрозрачна), под
|
||
указателем и с фокусом клавиатуры внутри карточки — в полную яркость; стрелка связи, у которой хотя бы один конец — такая задача, SHALL быть приглушена так же.
|
||
Приглушение — только вид: модель, видимая выборка, критический путь, проблемы, раскладка и экспорт JSON от него не
|
||
зависят; переключателя нет; правило одно для данных хаба, CSV и JSON.
|
||
При возврате на `/app/board` с другой страницы приложения в той же вкладке доска SHALL восстанавливать прежнюю
|
||
прокрутку; после перезагрузки страницы доска открывается сверху.
|
||
|
||
Дорожки SHALL разделяться линией; фон развёрнутых дорожек один, без чередования, чтобы подсветка колонок «по датам»
|
||
(прошедшие даты, сегодня) была видна на всех дорожках; свёрнутая дорожка выделяется фоном. У карточки задачи, статус
|
||
Jira которой не входит в `closed_statuses`, с плановой датой окончания раньше сегодняшней дата «до …» SHALL быть цветом
|
||
ошибки, с подсказкой и текстом для экранного чтения «просрочено»; это только вид, модель и раскладка от него не зависят.
|
||
|
||
#### Scenario: Раскладка переживает переход
|
||
- **WHEN** раскладка карточек сохранена страницей `links.html`, открыта `/app/board` с теми же данными
|
||
- **THEN** карточки стоят на тех же местах, дорожки в том же порядке и свёртке, фильтры те же
|
||
|
||
#### Scenario: Совпадение со старой доской
|
||
- **WHEN** на одной фикстуре `GET /board` и одном наборе фильтров построены модель, видимая выборка, критический путь
|
||
и проблемы старой страницей и приложением
|
||
- **THEN** результаты совпадают
|
||
|
||
#### Scenario: Открыть карточку
|
||
- **WHEN** на карточке AURORA-1620 нажат значок «Открыть карточку»
|
||
- **THEN** адрес `/app/issues/AURORA-1620`, карточка задачи открыта окном, под ним доска
|
||
|
||
#### Scenario: Возврат с карточки
|
||
- **WHEN** доска прокручена вниз, нажат значок «Открыть карточку», затем окно закрыто
|
||
- **THEN** адрес `/app/board`, доска показана с той же прокруткой и раскладкой, без повторной загрузки `GET /board`
|
||
|
||
#### Scenario: Возврат из другого раздела
|
||
- **WHEN** доска прокручена вниз, выбран раздел «Задачи», затем раздел «Доска связей»
|
||
- **THEN** доска показана с той же прокруткой
|
||
|
||
#### Scenario: Закрытые на доске приглушены
|
||
- **WHEN** на доске AURORA-1620 «В работе», AURORA-1621 «Готово», AURORA-1622 «Снято», связь AURORA-1620 → AURORA-1621
|
||
- **THEN** карточки AURORA-1621 и AURORA-1622 и стрелка AURORA-1620 → AURORA-1621 приглушены, AURORA-1620 — нет;
|
||
под указателем AURORA-1621 — в полную яркость; критический путь и ящик «Проблемы» те же, что без приглушения
|
||
|
||
#### Scenario: Свёрнутая легенда
|
||
- **WHEN** в легенде скрыт вид работы «тестирование», легенда свёрнута, страница перезагружена
|
||
- **THEN** легенда свёрнута, рядом с кнопкой «Легенда» — «скрыто: 1»; карточек вида «тестирование» на доске нет
|
||
|
||
#### Scenario: Просрочка на доске
|
||
- **WHEN** сегодня 22.10, у AURORA-1620 статус «В работе» и срок 20.10, у AURORA-1621 «Готово» и срок 20.10, включено
|
||
«По датам»
|
||
- **THEN** дата AURORA-1620 — цветом ошибки с пометкой «просрочено», у AURORA-1621 — обычная; подсветка колонки 20.10
|
||
видна на всех дорожках, дорожки одного фона
|
||
|
||
### Requirement: Сборка и разработка
|
||
Приложение SHALL быть npm-проектом в `web/` (React, TypeScript, Vite) с точными версиями зависимостей и
|
||
`package-lock.json`. `make web` SHALL устанавливать зависимости (`npm ci`) и собирать приложение в
|
||
`src/sdlc_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи
|
||
задач и исходники), иконки и прочие ресурсы страницы SHALL входить в сборку: приложение не обращается к внешним
|
||
адресам. `make check` SHALL запускать `uv run pytest`, проверку типов и линтер фронта и тесты фронта; зелёным считается
|
||
только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `SDLC_HUB_URL` (по
|
||
умолчанию тестовый `http://127.0.0.1:8791`).
|
||
|
||
#### Scenario: Сборка с нуля
|
||
- **WHEN** в чистом checkout выполнен `make web`
|
||
- **THEN** в `src/sdlc_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки
|
||
|
||
#### Scenario: Без интернета
|
||
- **WHEN** приложение открыто на машине без доступа в интернет
|
||
- **THEN** страница не запрашивает ресурсов вне хаба, текст набран Mulish, ключи задач — IBM Plex Mono
|