Andrey Epifancev 6a2d8b7f5e spec(web-modules): веб под модули — состояние модулей, «Обзор», адреса модулей
Change web-modules по аудиту веба 24.10.2026 (#98).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:38:46 +04:00

13 KiB
Raw Blame History

Context

Основа — main после слияния sdlc-rename (#97): пакет sdlc_hub, бренд «SDLC Hub», ключи sdlc-hub-*. Ветка feat/98-web-modules перебазируется на него до исполнения плана.

Аудит веба 24.10.2026 (сабагент, код + живой хаб, скриншоты в scratchpad сессии) — главное:

  • контракт HubModule (web/src/modules/types.ts): name, pages, nav, Provider, SidebarStatus (компонент), TopbarNotice, useOverlay, projectFields, useOnProjectCreated(batchId);
  • RefreshProvider (модуль jira, 174 строки) — единственный трекер пакетов: подхват пакетов viewer с первой командой refresh (по GET /batches/{id} на каждого кандидата), один опрос, остановка, перечитывание; агент — GET /agent (только jira); время — last_sync из снимка /board (грузится на каждой странице);
  • wiki: SyncStatus — текст из GET /projects + GET /wiki/status, кнопка только в шапке страницы;
  • ядро знает jira: / → /board в модуле jira, типы jira и fact_status в api/types.ts, api/closed.ts, бренд; модуль wiki импортирует fmtStamp из модуля jira;
  • modules/jira/issues/wiki/wiki.css (div.wiki h1…) перебивает заголовки страницы «Вики» (корень div.wiki);
  • POST /projects → один batch_id, оболочка раздаёт его всем модулям;
  • API: GET /agents уже есть (все поля агента), GET /projects уже несёт свежесть wiki, у пакета нет модуля.

Goals / Non-Goals

Goals: оболочка рисует состояние модулей по их описанию; пакеты и агенты отслеживает ядро; навигация и главная страница — модульные; граница ядра и модулей в вебе проверяется тестами; найденные аудитом дефекты страниц исправлены.

Non-Goals и Anti-Goals: см. proposal.md.

Decisions

  1. Навигация — группы по модулям + «Обзор» (вариант «А + Б» аудита). Группы оставляют все разделы на виду (их четыре), «Обзор» даёт модульную главную вместо /board и место для сводки. Переключатель модулей (вариант «В») отклонён: лишний щелчок между доской и вики ради масштаба, которого нет.
  2. Адреса с префиксом модуля (/app/jira/…, /app/wiki) и перенаправления со старых. Делается сейчас, пока ломается всё сразу (переименование); пути модулей не пересекаются по построению. Перенаправления объявляет модуль jira (это его прежние адреса). GET / хаба — на /app/.
  3. Состояние модуля — данные (ModuleStatus), рисует ядро. Компонент SidebarStatus убирается: из-за него блок и был разнобойным. Свежесть берётся из уже загруженного GET /projects чистой функцией от проекта — без запросов модуля: jira — jira_synced_at (новое поле, общее для проектов), wiki — wiki_synced_at/wiki_sync_error.
  4. Один трекер пакетов в ядре (shell/activity/): обобщение RefreshProvider — состояние на модуль (event, active, busy), опрос GET /batches/{id}, правила остановки прежние. Подхват — по полю module из GET /batches?module= (новое), любого автора: автообновления хаба и пакеты из MCP теперь видны (С1/В1 аудита), а подхват не делает запрос на каждого кандидата. Прежнее правило «только viewer + refresh» отменено (сценарий «Пакет с другими командами» меняется: пакет показывается, но не блокирует кнопку).
  5. Кнопка недоступна только на время пакета своего действия обновления (jira — первая команда refresh, wiki — wiki_sync; тип виден в GET /batches/{id}, который трекер и так опрашивает). Иначе долгое скачивание вложения блокировало бы «Обновить из Jira».
  6. Обновление: значок в строке модуля + вторичная кнопка в шапке страниц модуля, primary для обновления нет. Сейчас на «Вики» две зелёные кнопки (в панели и в шапке) — против правила AURA «одна primary на экран».
  7. Агенты — один опрос GET /agents раз в 60 с; точка агента: на связи / офлайн / «обновите агента» (warning или update_hint), всегда с текстом.
  8. Узкий экран: в верхней строке «Меню», метка проекта и сводная точка; отдельная кнопка «Обновить из Jira» и TopbarNotice из контракта убираются (событие пакета показывает ядро строкой под верхней строкой). Выезжающая панель — модальная (#73 M-1…M-3).
  9. Пакеты нового проекта по модулям: POST/PATCH /projects отдают batches: [{module, id}]; batch_id и batch_ids остаются для MCP. Оболочка передаёт каждому модулю его пакет (снимает M-14/T5.3 #68 и потерю wiki_sync).
  10. Граница в вебе — тестами: импорты (ядро → только modules/index.ts; модуль → не другой модуль), уникальность имён модулей и nav.to (T7.5/T7.6 #90), пересечение имён классов CSS между модулями и с ядром. Префиксы классов не вводятся: тест на пересечение ловит именно случившийся дефект и не требует переименовывать сотни классов. div.wiki разметки Jira переименовывается в .jira-markup.
  11. Типы jira — в модуль jira (modules/jira/api/types.ts), closed.ts — туда же; fact_status удаляется из веба (веб-часть #60). В api/types.ts ядра остаются Batch, AgentView (полный вид из GET /agents), Project, CreatedProject, UpdatedProject, HubSettings без словарей jira.
  12. Форматтеры дат — ядро (ui/format.ts): fmtStamp («ДД.ММ ЧЧ:ММ»), fmtDate («ДД.ММ», не текущий год — «ДД.ММ.ГГГГ»). Списки и карточка перестают выводить ISO.
  13. «Обзор» берёт сводку у модуля (Summary({project})); счётчики jira — из снимка доски проекта (запрос только на «Обзоре»); вики — wiki_pages из проекта.
  14. Порядок архива: sdlc-rename архивируется раньше web-modules: дельты web-app/hub-api этого change написаны поверх текстов после переименования.

Контракт веб-модуля (после change)

type ModuleStatus = {
  agent?: string;                                   // имя модуля агента в GET /agents
  scope: "all" | "project";                         // охват свежести и обновления
  freshness(p: Project): { available: boolean; at: string | null; error: string | null };
  refresh?: {
    label: string;                                  // «Обновить из Jira», «Обновить вики»
    start(project: string): Promise<{ id: number; estimate_seconds?: number }>;   // 409 → ApiError, ядро подхватит идущий
    isOwn(b: Batch): boolean;                       // пакет этого действия (по commands[0].type)
  };
  invalidate(qc: QueryClient, project: string | null): void;   // что перечитать после пакета модуля
};
type HubModule = {
  name: string; title: string;                      // «jira» / «Jira»
  pages: RouteObject[]; nav: NavItem[];             // nav.to — с префиксом модуля
  status?: ModuleStatus;
  Summary?: ComponentType<{ project: Project }>;    // карточка «Обзора»
  Provider?, useOverlay?, projectFields?;           // как было; SidebarStatus, TopbarNotice, useOnProjectCreated — удалены
};

Risks / Trade-offs

  • Подхват пакетов любого автора покажет и пакеты, поставленные из чата (claude), — это желаемо, но строка «Jira» будет чаще «занята». Кнопка при этом не блокируется (решение 5).
  • Счётчики на «Обзоре» грузят снимок доски — тяжёлый запрос; только на этой странице и с тем же ключом кэша, что у доски.
  • Смена адресов ломает закладки — их держат перенаправления; внешних ссылок на веб нет (MCP веб-адресов не отдаёт).

Decisions made without discussion

Владелец 24.10.2026 ответил «делай» на вопросы brainstorming — решения 1–14 выше приняты агентом по аудиту.

# Дата Решение Обоснование Цена ошибки
1 24.10.2026 Навигация — группы + «Обзор» 4 раздела, 2 модуля; нужна модульная главная переделка панели
2 24.10.2026 Адреса /app/<модуль>/… с перенаправлениями заодно с переименованием, пути не пересекаются вернуть пути, перенаправления наоборот
3 24.10.2026 Состояние модуля — данные, рисует ядро разнобой блоков возник из компонента —
4 24.10.2026 Трекер в ядре, подхват любого автора по module видны автообновления и пакеты MCP вернуть фильтр по автору
5 24.10.2026 Блокирует только пакет своего обновления долгие пакеты не мешают обновлению —
6 24.10.2026 Обновление — значок в панели + secondary в шапке одна primary на экран перекраска кнопок
7 24.10.2026 Один опрос /agents агент wiki не виден сейчас —
8 24.10.2026 Узкий экран: метка проекта + сводная точка проект не виден, две кнопки обновления вернуть кнопку
9 24.10.2026 batches по модулям, старые поля остаются пакет wiki терялся; MCP не ломать —
10 24.10.2026 Тест на пересечение классов CSS вместо префиксов ловит дефект без массового переименования ввести префиксы позже
11 24.10.2026 Типы jira — в модуль, fact_status из веба удалён граница ядра; #60 вернуть поле в тип
12 24.10.2026 Форматтеры дат в ядре три формата дат, импорт wiki → jira —
13 24.10.2026 Сводка «Обзора» от модуля ядро не знает данных модулей —
14 24.10.2026 Архив после sdlc-rename дельты поверх новых имён правка дельт при архиве