33 KiB
Веб под модули — план реализации
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: оболочка веба рисует состояние модулей (агент, свежесть, обновление, пакет) по их описанию, навигация сгруппирована по модулям с главной страницей «Обзор», адреса модулей с префиксом модуля, граница ядра и модулей проверяется тестами, дефекты страниц из аудита исправлены.
Architecture: контракт HubModule получает title, status: ModuleStatus (данные) и Summary; ядро (web/src/shell/activity/) держит один опрос GET /agents и один трекер пакетов на все модули (обобщение RefreshProvider модуля jira); блок состояния, «Обзор», верхняя строка узкого экрана — компоненты ядра. Бэкенд добавляет jira_synced_at в проект, module в пакет и batches в ответы POST/PATCH /projects.
Tech Stack: React 19 + TypeScript + Vite, TanStack Query, react-router, Vitest + Testing Library, Playwright; Python 3.13 (uv, pytest).
Spec: openspec/changes/web-modules/ (proposal.md, design.md, specs/web-app, specs/hub-api) — issue jira-hub#98. Аудит и решения — design.md.
Global Constraints
- Предусловие: ветка перебазирована на
mainпосле слияния sdlc-rename (#97): пакетsrc/sdlc_hub/, бренд «SDLC Hub», ключиsdlc-hub-*,readMigratedвweb/src/api/legacyStorage.ts. Без этого план не начинать. - Адреса: «Обзор»
/(под/app), jira —/jira/board,/jira/issues,/jira/issues/:key,/jira/feed; wiki —/wiki; прежние/board,/issues,/issues/:key,/feed— перенаправление с сохранениемsearch,replace. - Тексты (точно): названия модулей «Jira», «Вики»; «Обзор»; агент — «агент на связи» / «агент офлайн» / «обновите агента»; свежесть — «данные на ДД.ММ ЧЧ:ММ · все проекты» / «данные на ДД.ММ ЧЧ:ММ · проект», «обновления ещё не было»; действия — «Обновить из Jira», «Обновить вики»; подсказки значков — «Обновить из Jira — все проекты», «Обновить вики — проект <метка>»; заголовок вкладки «<страница> · <метка> · SDLC Hub»; «Последние пакеты проекта»; «Команды хаба»; «Привязать корень вики»; «нужно целое число».
- События пакета — прежние тексты
refreshText(пакет #N в очереди/выполняется…,выполнен,не выполнен: …,отменён,…: <причина> — опрос остановлен, «<действие>» снова доступна,хаб не отвечает — пакет не поставлен);POLL_MS = 3000,MAX_POLL_ERRORS = 5,DONE_VISIBLE_MS = 10_000; подхват иGET /agents— раз в60_000мс. - Primary-кнопок обновления нет: значок в строке модуля,
variant="secondary"в шапках страниц. - Цвет не единственный признак: точка агента и просрочка — с текстом (
title/скрытый текст). - AURA (скилл
aura-design): цвета только изstyles/tokens.css, компоненты изweb/src/ui/. - Зелёный — весь набор:
uv run pytest -q,cd web && npm run lint && npm test; перед финальным ревью ещёcd web && npm run e2e. - Живой хаб (:8790) и его данные не трогать; e2e — только на тестовом хабе (
web/scripts/test-hub.sh, :8791). - Язык комментариев, текстов, коммитов — русский; коммиты заканчиваются
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>.
Review Focus
- Пакет, поставленный из MCP или автообновлением, во время открытой страницы: строка модуля должна его показать не позже чем через 60 с и не блокировать кнопку обновления — Task 3,
picks up batch of any authorиforeign batch does not block refresh. - Смена проекта во время идущего пакета вики: строка «Вики» нового проекта не показывает чужой пакет, опрос старого не течёт в новую строку — Task 3,
project switch drops project-scoped batch. - Проект без корня вики на всех поверхностях (панель развёрнута/свёрнута, телефон, «Обзор»): нет пустой строки «Вики», нет запроса
/wiki/statusиз оболочки — Task 4no wiki row without root, Task 5overview card without root. - Прежний адрес карточки из истории браузера (
/app/issues/KEYбез проекта): перенаправление и окно поверх списка, закрытие окна возвращает на/jira/issuesс текущим проектом — Task 4,legacy card address. - Пересечение классов CSS, которое уже ломало «Вики»: тест-страж должен падать на
div.wiki-подобном конфликте и на дубле класса между модулем и ядром — Task 2,css classes do not collide.
Task 1: API для блока состояния
Files:
- Modify:
src/sdlc_hub/modules/jira/project_fields.py(view:jira_synced_at),src/sdlc_hub/core/queue.py(batch_view:module;list_batches(conn, limit=20, project=None, module=None)),src/sdlc_hub/core/api.py(h_batches_get:?module=, неизвестный — 404not_found;POST /projectsиPATCH /projects/{label}:batches;GET /иGET /links→Location: /app/) - Test:
tests/test_project_fields.py,tests/test_queue_core.py,tests/test_api.py,tests/test_web_app.py,tests/test_wiki_projects.py
Interfaces:
-
Produces:
GET /projects[].jira_synced_at: str | null—finished_atпоследнегоsync_runполного обновления (тот же запрос, чтоlast_syncвmodules/jira/mirror.pyснимка доски — вынести в функциюlast_full_sync(conn) -> dict | Noneмодуля jira и использовать в обоих местах);batch_view(...)["module"]: str | None—registry.module_of(<тип первой команды по seq>).name;POST /projects→{project, batch_id, batches: [{"module": str, "id": int}]};PATCH→{project, batch_ids, batches}. -
Step 1: Тесты
test_projects_jira_synced_at: до полного обновленияGET /projects→ у проектаjira_synced_at is None; после завершённого полногоrefreshсsync_run.finished_at = "2026-10-24T11:07:00"→ это значение у каждого проекта.test_batch_module: пакетrefresh→module == "jira"; пакетwiki_sync→"wiki"; пакет без команд →None(вlist_batchesиbatch_view).test_batches_module_filter:GET /batches?module=wiki&project=AWPCA→ только пакеты wiki проекта;GET /batches?module=nope→ 404.test_create_project_batches:POST /projects {label, name, wiki_root: "DOC-001234"}→batches == [{"module": "jira", "id": <refresh>}, {"module": "wiki", "id": <wiki_sync>}],batch_id == <refresh>;PATCH /projects/AWPCA {wiki_root}→batches == [{"module": "wiki", "id": batch_ids[0]}].test_root_redirect(вtest_web_app.py, заменить прежний):GET /иGET /links→ 302,Location: /app/.
-
Step 2:
uv run pytest tests/test_project_fields.py tests/test_queue_core.py tests/test_api.py tests/test_web_app.py tests/test_wiki_projects.py -q— FAIL. -
Step 3: реализация по Interfaces. В ядре (
queue.py,api.py) — без названий модулей (границаtests/test_registry.py): модуль берётся изregistry.module_of. -
Step 4:
uv run pytest -q— весь набор PASS (в том числеtest_registry.py, эталоны MCP:batch_idв MCP не меняется). -
Step 5: Commit —
feat(web-modules): API — свежесть Jira в проекте, модуль пакета, пакеты нового проекта по модулям.
Task 2: Граница ядра и модулей в вебе
Files:
- Create:
web/src/ui/format.ts(+format.test.ts),web/src/modules/jira/api/types.ts,web/src/boundary.test.ts - Move:
web/src/api/closed.ts(+ тест) →web/src/modules/jira/api/closed.ts - Modify:
web/src/api/types.ts(остаютсяBatch(+module: string | null),BatchCommand,BatchStatus,AgentView,CreatedBatch,Project,CreatedProject,UpdatedProject,HubSettingsбез словарей jira;fact_statusудалён везде, в том числеweb/src/test/issueFixtures.tsи комментарийmodules/jira/issues/card.ts), импорты типов в модулях;modules/jira/board/model/util.ts(fmtStampпереезжает вui/format.ts),modules/wiki/PageView.tsx,modules/wiki/SyncStatus.tsx;modules/jira/issues/wiki/wiki.cssи разметка, использующаяclassName="wiki"в модуле jira →jira-markup;IssueTable.tsxиIssueCard.tsx(даты черезfmtDate);web/src/modules/index.tsилиmodules.test.tsx(уникальность)
Interfaces:
-
Produces:
fmtStamp(iso: string | null): string— «ДД.ММ ЧЧ:ММ» (как сейчас вboard/model/util.ts);fmtDate(iso: string | null, now?: Date): string— «ДД.ММ», год не текущий — «ДД.ММ.ГГГГ»,null— «—»;AgentView= поляGET /agents:{module: string; online: boolean; last_seen_at: string | null; agent_version: number | null; warning: string | null; update_hint: string | null; current_command_id: number | null; release: {version: number} | null}. -
Step 1: Тесты
format.test.ts:fmtDate("2026-10-19", new Date("2026-10-24"))→"19.10";fmtDate("2025-12-31", new Date("2026-10-24"))→"31.12.2025";fmtDate(null)→"—";fmtStamp("2026-10-24T11:07:00")→"24.10 11:07".boundary.test.ts(читает файлы черезimport.meta.glob(..., { query: "?raw", eager: true })):core does not import modules: файлыsrc/{shell,ui,api,projects,auth,styles}/**/*.{ts,tsx}(кроме тестов) не импортируютmodules/иначе какmodules/index.tsиmodules/types.ts;module does not import other module: файлmodules/<a>/**не импортируетmodules/<b>/;css classes do not collide: множество имён классов (/\.([a-zA-Z_][\w-]*)/gпо селекторам, без значений свойств) вmodules/<a>/**/*.cssне пересекается сmodules/<b>/**/*.cssи сsrc/{shell,ui,styles,projects,auth}/**/*.css; сообщение — имя класса и файлы;core has no module terms: в файлах ядра нетfact_status,closed_statuses,kind_labels,link_types,BoardIssue,IssueRow.
modules.test.tsx:names uniqueиnav paths unique(T7.6) — сообщение с дублем;NotFound— ключ ссылки уникален при дубле (T7.5).
-
Step 2:
cd web && npx vitest run src/ui/format.test.ts src/boundary.test.ts src/modules/modules.test.tsx— FAIL (пересечение.wiki, импорт wiki → jira, термины вapi/types.ts). -
Step 3: перенос типов и
closed.ts,ui/format.ts,jira-markup, даты в таблице и карточке, удалениеfact_status. -
Step 4:
cd web && npm run lint && npm test— PASS; на странице «Вики» тестового хаба заголовок h1 — 20 px (проверить в e2e Task 8). -
Step 5: Commit —
refactor(web-modules): граница ядра и модулей в вебе, форматтеры дат в ядре, без fact_status.
Task 3: Контракт состояния модуля и трекер пакетов в ядре
Files:
- Create:
web/src/shell/activity/ActivityProvider.tsx,web/src/shell/activity/activity.ts(контекст и хуки),web/src/shell/activity/events.ts(переносrefreshStatus.ts:ModuleEvent,eventText(e, actionLabel), константы),web/src/shell/activity/activity.test.tsx(перенос и обобщениеmodules/jira/refresh/refresh.test.tsx),web/src/modules/jira/status.ts,web/src/modules/wiki/status.ts - Modify:
web/src/modules/types.ts(ModuleStatus,HubModule.title,status,Summary; удалитьSidebarStatus,TopbarNotice,useOnProjectCreated),web/src/modules/index.ts,web/src/modules/jira/index.tsx,web/src/modules/wiki/index.tsx,web/src/App.tsx(провайдер ядра),web/src/modules/wiki/api/queries.ts(useWikiSyncне нужен оболочке; страница вики берёт действие из трекера) - Delete:
web/src/modules/jira/refresh/(RefreshProvider.tsx,refresh.ts,refreshStatus.ts,DataStatus.tsx,SidebarStatus.tsx, их тесты) после переноса тестов;web/src/modules/wiki/SyncStatus.tsx(+ тест)
Interfaces:
-
Consumes:
Batch.module,AgentView(Task 2),GET /batches?module=&project=&limit=,jira_synced_at(Task 1). -
Produces:
ModuleStatus— как в design.md («Контракт веб-модуля»):agent?: string; scope: "all" | "project"; freshness(p: Project): {available: boolean; at: string | null; error: string | null}; refresh?: {label: string; start(project: string): Promise<{id: number; estimate_seconds?: number}>; isOwn(b: Batch): boolean}; invalidate(qc: QueryClient, project: string | null): void.useModuleActivity(name: string): {agent: AgentView | null; event: ModuleEvent | null; busy: boolean; running: Batch | null; start(): Promise<void>; track(id: number): void}—busy: постановка в пути или отслеживаемый пакет удовлетворяетrefresh.isOwn.useAgents(): AgentView[];useActivitySummary(): "error" | "busy" | "online" | "offline"(для сводной точки).ActivityProvider({children, project, pollMs?})—project— текущая метка (изProjectContext).- jira:
status = {agent: "jira", scope: "all", freshness: p => ({available: true, at: p.jira_synced_at ?? null, error: null}), refresh: {label: "Обновить из Jira", start: () => api.post("/refresh", {full: true, author: "viewer"}), isOwn: b => b.commands?.[0]?.type === "refresh"}, invalidate: board-все, issues, issue, changes}; wiki:{agent: "wiki", scope: "project", freshness: p => ({available: !!p.wiki_root, at: p.wiki_synced_at ?? null, error: p.wiki_sync_error ?? null}), refresh: {label: "Обновить вики", start: project => api.post("/wiki/sync", {project, author: "viewer"}), isOwn: b => b.commands?.[0]?.type === "wiki_sync"}, invalidate: ["wiki", project] tree/page/search}.
-
Правила трекера: подхват — при монтировании, при смене проекта, раз в 60 с и после
start:GET /batches?module=<m>&limit=20(+&project=<метка>дляscope: "project"), первый незавершённый (queued/running, самый старый) — если ничего не отслеживается; при смене проекта отслеживаемый пакет модуля сscope: "project"сбрасывается без события.start: агент офлайн поuseAgents()→agentOffline: trueв событии; ответ 409 (ApiError.status === 409) — сразу подхват без события ошибки. Если отслеживается чужой пакет (неisOwn),startставит пакет и переключает отслеживание на него. По завершении пакета — событие,module.status.invalidate(qc, project), перечитывание["projects"]. -
Step 1: Тесты
activity.test.tsx(мокfetchкак в прежнемrefresh.test.tsx, фейковые таймеры) — перенести все сценарии прежнего файла на модуль jira (M-2,M-3,M-4,T10.2,T4.1, 401,batch-doneгаснет, переход между страницами), заменив «подхват viewer+refresh» новыми:picks up batch of any author:GET /batches?module=jiraотдаёт пакет автораhubвrunning→event.kind === "batch", опросGET /batches/{id};foreign batch does not block refresh: отслеживается пакет jira сcommands[0].type = "ensure_fields"→busy === false;start()→ одинPOST /refresh,event.id— новый пакет;project switch drops project-scoped batch: отслеживаетсяwiki_syncпроектаAWPCA,project→STOCKS_DRRK→event === null, опрос пакета AWPCA остановлен; jira-пакет не сброшен;wiki 409 tracks running batch:POST /wiki/sync→ 409 → подхват идущего пакета, события ошибки нет;agent offline from /agents:GET /agents→ wikionline: false;start()для wiki → событие сagentOffline: true;invalidates module data only: завершение пакета wiki инвалидирует["wiki", "AWPCA", …]и["projects"], не["board"];one agents poll: два модуля → один запросGET /agentsза интервал.
-
Step 2:
cd web && npx vitest run src/shell/activity— FAIL. -
Step 3: реализация. Состояние — на модуль (
Record<name, {active, event, starting, failures}>), опросы пакетов —useQueriesпо активным; логика остановки — перенос изRefreshProviderбез изменений. -
Step 4:
cd web && npm run lint && npm test— PASS (временные заглушки оболочки, если Task 4 ещё не сделан:Sidebarможет рендерить старый блок черезuseModuleActivity— главное, чтобы набор был зелёным). -
Step 5: Commit —
feat(web-modules): состояние модуля в контракте, трекер пакетов и агенты в ядре.
Task 4: Оболочка — группы, блок состояния, узкий экран, адреса
Files:
- Create:
web/src/shell/ModuleStatusBlock.tsx(+ тест),web/src/shell/useDocumentTitle.ts - Modify:
web/src/shell/Sidebar.tsx(+Sidebar.test.tsx),web/src/shell/Layout.tsx,web/src/shell/NotFound.tsx,web/src/shell/shell.css,web/src/modules/jira/index.tsx(пути/jira/…, перенаправления прежних путей),web/src/modules/jira/issues/modal.tsиoverlay.tsx(/jira/issues, закрытие окна — с текущим проектом), все ссылки на/board,/issues,/feedвнутри модулей - Test:
web/src/shell/*.test.tsx,web/src/projects/projects.test.tsx
Interfaces:
-
Consumes:
useModuleActivity,useAgents,useActivitySummary(Task 3);HubModule.title,status. -
Produces:
NavItem.title?: string(название страницы для вкладки; по умолчаниюlabel);useDocumentTitle(page: string | null, project: string | null);ModuleStatusBlock({project, variant: "expanded" | "collapsed"}); оболочка: пункт «Обзор» (to: "/") — первым, затем группыMODULESс заголовкомtitle; смена проекта вне раздела →/. -
Step 1: Тесты
ModuleStatusBlock.test.tsx: две строки «Jira» и «Вики» с точкой и текстом агента («агент на связи»/«агент офлайн»/«обновите агента»,warningвtitle); «данные на 24.10 11:07 · все проекты» и «данные на 24.10 11:05 · проект»; значок сaria-label«Обновить из Jira — все проекты» и «Обновить вики — проект AWPCA»;jira_synced_at: null→ «обновления ещё не было»;wiki_sync_error: "403 Forbidden"→ текст ошибки в строке «Вики» (тон ошибки, с текстом);no wiki row without root— проект безwiki_root→ одна строка, запросов/wiki/statusи/boardнет;collapsed— по значку на модуль, подробности вtitle.Sidebar.test.tsx: заголовки групп «Jira», «Вики»; «Обзор» первым; на 390 px верхняя строка — «Меню», метка проекта, сводная точка (aria-labelс состоянием);drawer is modal— фокус в панели, Escape во вложенном меню закрывает только меню (M-1 #73), Escape после — панель, фокус на «Меню»; метка проекта свёрнутой панели целиком вtitle(M-4).Layout/projects.test.tsx:legacy card address—/issues/AURORA-1?project=AWPCA→/jira/issues/AURORA-1?project=AWPCA, окно поверх списка, закрытие →/jira/issues?project=AWPCA;/board?project=X→/jira/board?project=X; смена проекта на «Вики» у проекта без вики → остаётся «Вики» (страница с формой привязки), на неизвестном адресе →/; заголовок вкладки «Вики · AWPCA · SDLC Hub», «Обзор · AWPCA · SDLC Hub».
-
Step 2:
cd web && npx vitest run src/shell src/projects— FAIL. -
Step 3: реализация. Панель прокручивается сама, если не влезает (M-5 #73); удалить мёртвый
--topbar-h(M-6), устаревшие комментарии про верхнюю панель (M-7 #73, С19 аудита); событие пакета на узком экране — строкой под верхней строкой (заменаTopbarNotice), на развёрнутой панели — в строке модуля;role="status"— постоянный контейнер (T10.1 #94). -
Step 4:
cd web && npm run lint && npm test— PASS. -
Step 5: Commit —
feat(web-modules): боковая панель по модулям, блок состояния, узкий экран, адреса модулей.
Task 5: Страница «Обзор»
Files:
- Create:
web/src/shell/OverviewPage.tsx(+OverviewPage.test.tsx),web/src/modules/jira/Summary.tsx,web/src/modules/wiki/Summary.tsx - Modify:
web/src/shell/pages.tsx(маршрут/→ «Обзор» подProjectGate),web/src/modules/jira/index.tsx,web/src/modules/wiki/index.tsx(Summary),web/src/api/queries.ts(useRecentBatches(project)→GET /batches?project=&limit=10)
Interfaces:
-
Consumes:
ModuleStatusBlockнет — карточка модуля «Обзора» рисует ядро изuseModuleActivity+status.freshness+useAgents(версияagent_version), кнопкаvariant="secondary";Summary({project: Project}). -
Produces: jira
Summary: « задач · <открытых> открытых · <просроченных> просрочено» (поuseBoard(project): свои задачиneighbor = false; открытые — неclosedпоmodules/jira/api/closed.ts; просроченные — открытые сduedate< сегодня), ссылки «Доска связей», «Задачи», «Лента»; wikiSummary: «<wiki_pages> страниц», поле поиска (Enter →/wiki?project=<метка>&q=<строка>; страница «Вики» читаетqв поле поиска), ссылка «Вики»; без корня — объяснение и ссылка «Привязать корень вики» на/wiki?project=<метка>. -
Step 1: Тесты
OverviewPage.test.tsx:/с проектомAWPCA(корень есть) — две карточки «Jira», «Вики» с агентом и версией («v14», «v1»), свежестью, кнопками «Обновить из Jira», «Обновить вики» (обеsecondary);overview card without root—sfm_kedr→ карточка «Вики» с «Привязать корень вики»; «Последние пакеты проекта» — строки#<id>, модуль, название, автор, статус, время; поиск по вики Enter → адрес/wiki?project=AWPCA&q=согласов, на «Вики» строка поиска заполнена и показаны результаты. -
Step 2:
cd web && npx vitest run src/shell/OverviewPage.test.tsx— FAIL. -
Step 3: реализация (сетка карточек: 2 колонки ≥ 1024 px, 1 колонка уже; компоненты
Panel,PageHeader,Button). -
Step 4:
cd web && npm run lint && npm test— PASS. -
Step 5: Commit —
feat(web-modules): страница «Обзор» со сводками модулей и последними пакетами.
Task 6: Окно «Новый проект» по модулям
Files:
- Modify:
web/src/projects/NewProjectDialog.tsx(+ тест),web/src/projects/projects.css,web/src/api/queries.ts(useCreateProject,useUpdateProject— тип ответа сbatches),web/src/api/types.ts(CreatedProject.batches,UpdatedProject.batches: {module: string; id: number}[]),web/src/shell/Layout.tsx(onCreated→trackкаждого пакета своим модулем),web/src/modules/wiki/RootForm.tsx(пакет изbatches→trackмодуля wiki)
Interfaces:
-
Consumes:
useModuleActivity(name).track(Task 3),HubModule.title,projectFields. -
Step 1: Тесты
NewProjectDialog.test.tsx: группыfieldsetсlegend«Jira» и «Вики»; подсказки — текст под полем (aria-describedby),placeholderпустой; подсказка метки «метка задач в Jira и имя проекта в хабе»;51.5в «Номер доски Jira» → «нужно целое число», запроса нет (T4.1); ответbatchesс двумя пакетами →trackjira иtrackwiki вызваны со своими id (M-14, T5.3 #68);setExtra— функциональная форма (T4.2: два быстрых ввода в разные поля не теряют первое);FIELDSсобирается при отрисовке, не при импорте (T4.5);useUpdateProjectи показ 422 у поля корня — тесты (T4.4). -
Step 2:
cd web && npx vitest run src/projects src/modules/wiki— FAIL. -
Step 3: реализация.
-
Step 4:
cd web && npm run lint && npm test— PASS. -
Step 5: Commit —
feat(web-modules): «Новый проект» — поля по модулям, подсказки, пакеты каждому модулю.
Task 7: Страницы модулей — исправления по аудиту
Files:
-
Modify:
web/src/modules/wiki/WikiPage.tsx,Tree.tsx,Search.tsx,wiki.css(+ тесты);web/src/modules/jira/board/components/BoardHeader.tsx,web/src/modules/jira/issues/IssuesPage.tsx,IssueTable.tsx,issues.css,IssueCard.tsx,web/src/modules/jira/feed/FeedPage.tsx;web/src/projects/ProjectGate.tsx;web/src/styles/(глобальноеscrollbar-gutter: stableдля прокручиваемой области страницы) -
Step 1: Тесты
- «Вики»: шапка — h1 «Вики», подзаголовок «<wiki_root_title> · страниц · обновлено ДД.ММ ЧЧ:ММ», кнопка «Обновить вики»
secondaryизuseModuleActivity("wiki"); безpage— открыт корень и выделен в дереве; узлы-предки выбранной страницы раскрыты, после очистки поиска раскрытие прежнее (T10.2, T10.6);aria-setsize/aria-posinsetу узлов (T10.2); порядок заголовков h1 → h2 (T10.3); на 390 px (matchMediaмок) страница выше дерева; параметрqзаполняет поиск. - Доска, «Задачи», «Лента»: в шапке вторичная кнопка «Обновить из Jira» (
useModuleActivity("jira")), нет primary; под шапкой доски нет пустого блока.board-srcбез содержимого (N-1), мёртвые пропсыlabel/titleудалены (N-2 #68). - «Задачи»: просроченный срок — с текстом «просрочено» для экранного чтения (
visually-hidden) иtitle; срок черезfmtDate. - Карточка: раздел «Команды хаба» вместо «Команды моста».
ProjectGate: «проект NOPE не найден» и подсказка «выберите проект в списке «Проект» боковой панели (на узком экране — в «Меню»)».
- «Вики»: шапка — h1 «Вики», подзаголовок «<wiki_root_title> · страниц · обновлено ДД.ММ ЧЧ:ММ», кнопка «Обновить вики»
-
Step 2:
cd web && npx vitest run src/modules src/projects— FAIL. -
Step 3: реализация. Горизонтальная прокрутка «Задач» на 1440 px: таблица прокручивается внутри своей панели (
overflow-x: autoу обёртки таблицы, ширина обёртки не больше контейнера страницы). -
Step 4:
cd web && npm run lint && npm test— PASS. -
Step 5: Commit —
fix(web-modules): страницы модулей — шапки, «Вики» с корнем, просрочка, прокрутка «Задач».
Task 8: e2e, документация, спека
Files:
-
Modify:
web/e2e/*.spec.ts(новые адреса, «Обзор», блок состояния, перенаправления),web/e2e/hub.ts(фикстура пакетов, если нужна),ARCHITECTURE.md(раздел «Веб»: контрактHubModuleсstatus/Summary, трекер вshell/activity/, адреса),CLAUDE.md(адреса веба, «Обзор»),README.md(адреса веба) -
Test:
web/e2e/layout.spec.ts,web/e2e/screens.spec.ts, новыйweb/e2e/overview.spec.ts -
Step 1: Тесты e2e (тестовый хаб):
overview.spec.ts—/app/→/app/?project=…, две карточки, список пакетов;layout.spec.ts— на 1440 px у «Задач»document.documentElement.scrollWidth === window.innerWidth; на «Вики»getComputedStyle(h1).fontSize === "20px";/app/board→/app/jira/board; на 390 px — метка проекта в верхней строке, «Меню» модально;screens.spec.ts— скриншоты «Обзора» и панели в обеих темах (как прочие экраны). -
Step 2:
cd web && npm run e2e— новые тесты падают до правок e2e-хелперов, затем PASS. -
Step 3: документация.
-
Step 4:
uv run pytest -q,cd web && npm run lint && npm test && npm run e2e,openspec validate web-modules --strict— PASS. -
Step 5: Commit —
docs(web-modules): ARCHITECTURE, CLAUDE.md, README, e2e под модули.