Change web-modules по аудиту веба 24.10.2026 (#98). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 KiB
MODIFIED Requirements
Requirement: Оболочка и адреса
Приложение SHALL открываться под префиксом /app/. Главная страница — «Обзор» ядра (/app/?project=<метка>,
требование «Обзор проекта»). Страницы модулей SHALL лежать под префиксом имени модуля: модуль jira — /app/jira/board
(доска связей), /app/jira/issues (список задач), /app/jira/feed (лента изменений, требование «Лента изменений») и
/app/jira/issues/{key} (карточка задачи — окно поверх страницы, требование «Карточка задачи»); модуль wiki —
/app/wiki (требование «Вики проекта»). Прежние адреса /app/board, /app/issues, /app/issues/{key}, /app/feed
SHALL перенаправлять на новые с сохранением строки запроса, без новой записи истории. Неизвестный путь — страница «не
найдено» со ссылками на «Обзор» и разделы модулей.
Боковая панель SHALL быть закреплена у левого края окна на всю его высоту и MUST NOT прокручиваться вместе со
страницей; если её содержимое не помещается по высоте, прокручивается сама панель. Сверху вниз панель SHALL содержать:
название «SDLC Hub» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий отмечен, в
конце «+ Новый проект…»; требование «Проект в приложении»); пункт «Обзор»; разделы модулей группами — заголовок группы
— название модуля («Jira», «Вики»), под ним ссылки разделов модуля со значками (текущий отмечен
aria-current="page"); внизу — блок состояния модулей (требование «Состояние и обновление модулей»), переключатель
темы (требование «Тема») и меню с действием «Сменить токен». Свёрнутая панель SHALL быть узкой полосой с теми же
действиями значками: «Развернуть панель», метка текущего проекта (полностью — в подсказке), «Обзор», разделы
(группы разделены линией), по значку состояния на модуль, тема, меню; названия — в подсказках и aria-label. Свёртка
SHALL храниться в localStorage под ключом sdlc-hub-sidebar (collapsed; без записи — развёрнута).
На экранах уже 640 px панели в потоке нет: сверху SHALL быть закреплена строка с кнопкой «Меню», меткой текущего проекта и сводной точкой состояния модулей (ошибка, если она есть у какого-нибудь модуля; иначе — пакет в работе; иначе — все агенты на связи или нет), которая открывает панель. «Меню» открывает развёрнутую панель поверх страницы модальным окном: фокус переходит в панель и остаётся в ней, страница под ней недоступна; панель закрывается выбором раздела, клавишей Escape (если не открыто вложенное меню — тогда Escape закрывает только его) или щелчком мимо неё; фокус возвращается на «Меню». Горизонтальной прокрутки страницы на любой ширине нет.
Заголовок вкладки браузера SHALL быть «<название страницы> · <метка проекта> · SDLC Hub» (без проекта — без средней части); название страницы даёт оболочка («Обзор») или модуль. Неожиданная ошибка отрисовки страницы SHALL показываться сообщением с кнопкой «Перезагрузить», не пустым экраном.
Scenario: Переход между разделами
- WHEN на доске в боковой панели выбран раздел «Задачи» группы «Jira»
- THEN адрес —
/app/jira/issuesс текущим проектом, показан список задач; «назад» возвращает на доску
Scenario: Раздел «Лента»
- WHEN на доске в боковой панели выбран раздел «Лента»
- THEN адрес —
/app/jira/feedс текущим проектом, показана лента изменений за сегодня; раздел отмечен в панели
Scenario: Сменить токен
- WHEN в меню боковой панели выбрано «Сменить токен»
- THEN токен очищен, показан экран входа
Scenario: Панель на месте при прокрутке
- WHEN доска прокручена до самого низа
- THEN боковая панель видна целиком на прежнем месте: разделы и блок состояния модулей доступны без прокрутки вверх
Scenario: Свёрнутая панель
- WHEN нажата «Свернуть панель», страница перезагружена
- THEN панель свёрнута в полосу значков, доска шире; значок «Задачи» открывает список задач; видны два значка состояния — Jira и Вики; «Развернуть панель» возвращает полную панель
Scenario: Узкий экран
- WHEN ширина окна 390 px, нажата «Меню», в панели выбран раздел «Задачи»
- THEN панель открылась поверх страницы, фокус в панели; после выбора панель закрыта, фокус на «Меню», показан список задач; горизонтальной прокрутки нет
Scenario: Прежний адрес
- WHEN открыт
/app/issues/AURORA-1536?project=AWPCA - THEN адрес заменён на
/app/jira/issues/AURORA-1536?project=AWPCA, карточка открыта поверх списка задач
Scenario: Заголовок вкладки
- WHEN открыта страница «Вики» проекта
AWPCA - THEN заголовок вкладки — «Вики · AWPCA · SDLC Hub»
Requirement: Проект в приложении
«Обзор», доска, список задач, лента и вики SHALL показывать один проект — текущий. Текущий проект SHALL задаваться
параметром project в адресе страниц; без параметра — последний выбранный в этом браузере (localStorage), если он
есть в GET /projects, иначе первый из GET /projects; выбранный проект записывается в адрес без новой записи истории.
Проекта из адреса нет в GET /projects — страница SHALL показывать «проект <метка> не найден» и подсказку выбрать
проект в списке «Проект» боковой панели (на узком экране — в «Меню») вместо данных. Проектов нет — страницы SHALL
показывать приглашение создать проект («+ Новый проект…»). Переходы между страницами SHALL сохранять текущий проект;
смена проекта на странице, которой нет у нового проекта, открывает «Обзор». Карточка задачи
(/app/jira/issues/{key}) от проекта не зависит.
Доска SHALL загружать GET /board?project=<текущий>. Раскладка, дорожки и фильтры доски в localStorage SHALL
храниться отдельно для каждого проекта; у проекта AWPCA — на прежних ключах (раскладка, сделанная до проектов,
сохраняется). Фильтр спринтов SHALL показываться, только если хотя бы у одной своей задачи снимка (neighbor = false)
есть спринт; иначе — «колонки «Спринт» нет — показаны все задачи».
Форма «Новый проект» (пункт «+ Новый проект…» выпадающего списка) SHALL открываться окном поверх страницы: метка (с
подсказкой «метка задач в Jira и имя проекта в хабе») и название, затем поля каждого модуля в своей группе с
заголовком — названием модуля: «Jira» — номер доски Jira (необязательно; подсказка «scrum или kanban определится
сам»), «Вики» — корень вики (необязательно; подсказка «DOC-… или ссылка на страницу Eva»). Подсказка — текстом под
полем, не в placeholder. Пустое необязательное поле в запрос не попадает; числовое поле с нецелым значением — ошибка у
поля, запрос не уходит. «Создать» — POST /projects с автором viewer; ошибка хаба 409/422 — текст detail рядом с
кнопкой, окно остаётся открытым; успех — окно закрывается, текущим становится новый проект, каждый пакет из ответа
(batches) отслеживается своим модулем (требование «Состояние и обновление модулей»).
Scenario: Первый вход после выкатки
- WHEN открыт
/app/jira/boardбезproject, вlocalStorageвыбора нет, проектыAWPCAиSTOCKS_DRRK - THEN адрес —
/app/jira/board?project=AWPCA, доска —GET /board?project=AWPCAс прежней раскладкой карточек
Scenario: Переключение проекта
- WHEN на доске
AWPCAв выпадающем списке выбранSTOCKS_DRRK, затем открыт раздел «Задачи» - THEN доска —
GET /board?project=STOCKS_DRRKсо своей раскладкой; адрес «Задач» —/app/jira/issues?project=STOCKS_DRRK
Scenario: Неизвестный проект в адресе
- WHEN открыт
/app/jira/issues?project=NOPE - THEN «проект NOPE не найден» и подсказка выбрать проект в боковой панели; запроса
GET /issuesнет
Scenario: Создание проекта из формы
- WHEN в форме введены
STOCKS_DRRK, «Запасы ДРРК», доска и корень вики пусто; нажато «Создать» - THEN
POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", author: "viewer"}; текущий проект —STOCKS_DRRK; строка «Jira» блока состояния показывает «пакет #N выполняется»
Scenario: Проект с корнем вики из формы
- WHEN в форме введены
STOCKS_DRRK, «Запасы ДРРК», кореньDOC-001234; нажато «Создать» - THEN
POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", wiki_root: "DOC-001234", author: "viewer"}; строка «Jira» показывает пакет обновления, строка «Вики» — пакет синхронизации
Scenario: Метка занята
- WHEN в форме введена метка
awpca, хаб ответил 409 - THEN окно открыто, рядом с «Создать» — текст ошибки хаба
Scenario: Канбан-проект
- WHEN текущий проект —
STOCKS_DRRK, ни у одной своей задачи снимка нет спринта (у соседа изAWPCAспринт есть) - THEN вместо фильтра спринтов — «колонки «Спринт» нет — показаны все задачи»
Scenario: Нецелый номер доски
- WHEN в поле «Номер доски Jira» введено
51.5, нажато «Создать» - THEN у поля — «нужно целое число», запроса
POST /projectsнет
Requirement: Вики проекта
Приложение SHALL иметь страницу «Вики» (/app/wiki?project=<метка>, раздел группы «Вики» боковой панели; текущий
проект — как у остальных страниц, «Проект в приложении»). Шапка страницы: заголовок «Вики», подзаголовок «<название
корня> · страниц · обновлено ДД.ММ ЧЧ:ММ» и действие «Обновить вики» (вторичная кнопка; требование «Состояние и
обновление модулей»). Страница SHALL показывать:
- дерево страниц проекта (
GET /wiki/tree) слева, свёрнутое до первого уровня; узел раскрывается по клику; узел выбранной страницы и его предки раскрыты; - выбранную страницу справа (
GET /wiki/pages/{code}): заголовок, дата синхронизации, ссылка «открыть в Eva», Markdown с таблицами, без сырого HTML; безpageв адресе — корневая страница; ссылкиpage:<код>открывают страницу вики (код в адресе&page=<код>), ссылкиattachment:<id>скачивают вложение (запрос, опрос состояния, сохранение файла браузером), состояниеfailed— текст ошибки рядом со ссылкой; - на экранах уже 640 px выбранная страница — над деревом;
- поиск над деревом (
GET /wiki/search): список результатов с фрагментами вместо дерева, пока строка поиска не пуста; после очистки поиска раскрытие дерева прежнее; - проекта без корня — форму «Корень вики (DOC-… или ссылка)» с кнопкой «Привязать» (
PATCH /projects/{label}); 422 — текст ошибки у поля; - корень есть, синхронизаций нет — «вики ещё не синхронизирована» и действие «Обновить вики»;
- последняя синхронизация с ошибкой — плашку с текстом ошибки над страницей.
Scenario: Открыть страницу по ссылке
- WHEN на странице DOC-3 нажата ссылка
page:DOC-2 - THEN адрес
/app/wiki?project=AWPCA&page=DOC-2, справа DOC-2, в дереве DOC-2 выделен
Scenario: Проект без корня
- WHEN открыта
/app/wiki?project=sfm_kedr, корня нет; введеноDOC-001234, нажато «Привязать» - THEN
PATCH /projects/sfm_kedr {wiki_root: "DOC-001234"}; строка «Вики» блока состояния показывает ход пакета синхронизации
Scenario: Поиск
- WHEN в строке поиска «согласов»
- THEN вместо дерева — результаты
GET /wiki/search?project=AWPCA&q=согласов; очистка строки возвращает дерево
Scenario: Без выбранной страницы
- WHEN открыта
/app/wiki?project=AWPCAбезpage - THEN справа корневая страница проекта, в дереве она выделена
REMOVED Requirements
Requirement: Обновление из Jira
Reason: обновление и ход пакетов стали общими для модулей.
Migration: требование «Состояние и обновление модулей»; поведение «Обновить из Jira» (одна кнопка на приложение,
POST /refresh {full: true, author: "viewer"}, один опрос, подхват, остановка, перечитывание) сохраняется в нём для
модуля jira.
ADDED Requirements
Requirement: Состояние и обновление модулей
Каждый веб-модуль SHALL описывать своё состояние данными, а рисовать их SHALL ядро. Описание модуля: название для
показа («Jira», «Вики»); агент (имя модуля в GET /agents) или его отсутствие; свежесть данных для текущего проекта —
время, охват («все проекты» или «проект») и ошибка последнего обновления; действие обновления (название, охват,
постановка пакета) или его отсутствие; правило, какие пакеты принадлежат модулю; данные, которые надо перечитать после
пакета модуля; есть ли у модуля данные для проекта (у вики без корня — нет).
Ядро SHALL:
- опрашивать
GET /agentsпри входе и раз в 60 с — один опрос на приложение; агент модуля: «на связи», «офлайн» или «обновите агента» (естьwarningилиupdate_hint, текст — в подсказке); - вести один трекер пакетов на приложение: на модуль — не больше одного отслеживаемого пакета; пакет подхватывается при
входе, раз в 60 с и после действия обновления, если он не завершён, принадлежит модулю (поле
moduleвGET /batches) и текущему проекту (для модулей с охватом «проект») — любого автора, в том числе автообновления хаба и MCP; опросGET /batches/{id}каждые 3 с до терминального статуса; 4xx (404 — «пакета нет в хабе») и 5 ошибок «хаб не отвечает» подряд — опрос остановлен с сообщением; по завершении — перечитать данные модуля и список проектов; - ответ 409 на постановку пакета — отслеживать уже идущий пакет модуля, без сообщения об ошибке;
- агент модуля офлайн при обновлении — пакет всё равно ставится, событие «агент офлайн — пакет выполнится, когда он выйдет на связь; пакет #N в очереди…».
Блок состояния в развёрнутой боковой панели — по строке на модуль, у которого есть данные для проекта: название модуля,
точка агента (цвет и текст «агент на связи» / «агент офлайн» / «обновите агента» — не только цвет), кнопка-значок
обновления с подписью действия в подсказке и aria-label («Обновить из Jira — все проекты», «Обновить вики — проект
AWPCA»); под ними — «данные на ДД.ММ ЧЧ:ММ» с охватом («все проекты» / «проект»), «обновления ещё не было» или ошибка
последнего обновления; под ними — одно текущее событие пакета модуля: в очереди / выполняется (с оставшимся временем,
если хаб его дал, и пометками «агент офлайн», «хаб не отвечает, повтор»), выполнен (исчезает через 10 с), не выполнен
с текстом ошибки, отменён, опрос остановлен с причиной. Кнопка обновления модуля недоступна (с признаком загрузки), пока
запрос постановки в пути или выполняется пакет действия обновления модуля (у jira — пакет с первой командой refresh, у
wiki — wiki_sync); прочие пакеты модуля показываются, но обновление не блокируют: нажатие ставит пакет обновления, и
трекер переключается на него. Свёрнутая панель — по значку модуля с точкой агента, подробности в
подсказке, нажатие — обновление. На узком экране — сводная точка в верхней строке (требование «Оболочка и адреса»),
событие пакета — строкой под верхней строкой.
Модуль jira: агент jira; свежесть — jira_synced_at из GET /projects, охват «все проекты»; действие «Обновить из
Jira» — POST /refresh {full: true, author: "viewer"}; после пакета перечитываются снимки досок, списки задач, лента и
открытая карточка; доска, показывающая снимок из файла, его не заменяет. Модуль wiki: агент wiki; свежесть —
wiki_synced_at, wiki_sync_error из GET /projects, охват «проект»; данные для проекта есть, только если есть
wiki_root; действие «Обновить вики» — POST /wiki/sync {project, author: "viewer"}; после пакета перечитываются дерево,
страницы и поиск. Блок состояния MUST NOT загружать снимок доски или GET /wiki/status.
Страница модуля с действием обновления SHALL дублировать его вторичной кнопкой в шапке страницы; основной (primary) кнопки обновления в приложении нет.
Scenario: Обновление со списка задач
- WHEN открыт
/app/jira/issues, нажато «Обновить из Jira» в строке «Jira», пакет выполнен - THEN в хабе один пакет
refreshавтораviewer; список задач перезапрошен; строка «Jira» показывала «пакет #N выполняется», затем «пакет #N выполнен»; строка «Вики» не изменилась
Scenario: Один прогон на модуль
- WHEN нажата «Обновить из Jira» и, пока пакет выполняется, выполнен переход между разделами и нажата «Обновить из Jira» в шапке доски
- THEN кнопки «Обновить из Jira» недоступны, в хабе один пакет, его опрашивает один опрос
Scenario: Опрос получил 404
- WHEN опрос пакета вики получил 404
- THEN опрос остановлен, строка «Вики» показывает «пакета нет в хабе», «Обновить вики» снова доступна
Scenario: Автообновление хаба
- WHEN хаб поставил пакет «Автообновление» (автор
hub, модульjira), приложение открыто на странице «Вики» - THEN не позже чем через 60 с строка «Jira» показывает «пакет #N выполняется», по завершении — новое «данные на …»
Scenario: Пакет другого проекта
- WHEN идёт пакет
wiki_syncпроектаSTOCKS_DRRK, текущий проект —AWPCA - THEN строка «Вики» его не показывает, «Обновить вики» доступна
Scenario: Пакет с другими командами
- WHEN незавершённый пакет модуля jira автора
viewerсостоит из командensure_fields - THEN строка «Jira» показывает «пакет #N выполняется», кнопка «Обновить из Jira» доступна; её нажатие ставит пакет
refresh, и строка показывает его
Scenario: Агент офлайн
- WHEN агент
wikiофлайн и нажато «Обновить вики» - THEN пакет поставлен; строка «Вики» — точка с текстом «агент офлайн» и событие «агент офлайн — пакет выполнится, когда он выйдет на связь; пакет #N в очереди…»
Scenario: Синхронизация уже идёт
- WHEN нажато «Обновить вики», хаб ответил 409
- THEN строка «Вики» показывает идущий пакет, сообщения об ошибке нет
Scenario: Проект без корня вики
- WHEN текущий проект
sfm_kedrбез корня вики - THEN в блоке состояния только строка «Jira»; на свёрнутой панели — один значок состояния
Scenario: Агенту нужно обновление
- WHEN
GET /agentsотдаёт агентаjiraсwarning - THEN строка «Jira» — точка с текстом «обновите агента», текст
warningв подсказке
Requirement: Обзор проекта
Страница ядра «Обзор» (/app/?project=<метка>, пункт «Обзор» боковой панели) SHALL показывать шапку «Обзор» с
названием проекта и под ней карточку на каждый модуль, у которого есть данные для проекта: название модуля, агент (с
версией из GET /agents), свежесть с охватом, текущее событие пакета, вторичная кнопка обновления и сводка, которую
даёт модуль. Сводка jira: число своих задач проекта, из них открытых и просроченных (по снимку доски проекта), ссылки
«Доска связей», «Задачи», «Лента». Сводка wiki: число страниц, поле поиска по вики (Enter открывает «Вики» с этим
поиском), ссылка «Вики». Модуль без данных для проекта SHALL показывать карточку с объяснением и действием модуля (у
вики — «Привязать корень вики» ведёт на страницу «Вики»). Под карточками — «Последние пакеты проекта»: до 10 из
GET /batches?project=<метка>: номер, модуль, название, автор, статус, время создания.
Scenario: Главная
- WHEN открыт
/app/без проекта, последний выбранный —AWPCA - THEN адрес
/app/?project=AWPCA, две карточки — «Jira» и «Вики», список последних пакетов
Scenario: Поиск по вики с обзора
- WHEN на «Обзоре»
AWPCAв поле поиска вики введено «согласов» и нажат Enter - THEN открыта «Вики» с результатами поиска «согласов»
Scenario: Проект без вики
- WHEN «Обзор» проекта
sfm_kedrбез корня вики - THEN карточка «Вики» с объяснением и ссылкой «Привязать корень вики»
Requirement: Граница модулей в вебе
Ядро веба (web/src/{shell,ui,api,projects,auth,styles}) MUST NOT импортировать модули, кроме списка модулей
modules/index.ts, и MUST NOT содержать типов, справочников и терминов модулей (типы задач Jira, закрытые статусы,
словари видов работ и связей, «статус по факту»). Модуль MUST NOT импортировать другой модуль; общее (форматтеры дат
«ДД.ММ», «ДД.ММ ЧЧ:ММ», «ДД.ММ.ГГГГ» для дат не текущего года, скачивание файла) — в ядре. Имена модулей и адреса
разделов (to) SHALL быть уникальны. Имя класса CSS, объявленное в стилях модуля, MUST NOT объявляться в стилях
другого модуля или ядра. Даты в списке задач и карточке SHALL выводиться форматтерами ядра, не строкой ISO.
Scenario: Пересечение классов
- WHEN в
modules/jira/**.cssиmodules/wiki/**.cssобъявлен класс.wiki - THEN тесты фронта падают с именем класса и файлами
Scenario: Импорт чужого модуля
- WHEN файл в
modules/wiki/импортирует изmodules/jira/ - THEN тесты фронта падают с именем файла
Scenario: Дубль адреса раздела
- WHEN два модуля объявляют раздел с одинаковым
to - THEN тесты фронта падают с адресом и модулями