sdlc-hub/CLAUDE.md
Andrey Epifancev 2673bac5d8 docs: ядро и модули хаба; тест границы ядра (#87)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 23:20:06 +04:00

48 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# jira-hub
## Что это
Хаб моста Jira для АРМ КА: единственный источник истины по задачам проектов-меток Jira-проекта AURORA на Mac mini (`192.168.0.106`). Хранит зеркало задач, служебные заметки и очередь команд в SQLite, отдаёт HTTP API в локальной сети (агенту на виртуалке `jira-agent` и интерфейсам) и MCP-сервер для Claude. Главный сценарий: из чата поставить пакет команд, агент на виртуалке выполняет их в Jira по правилу «проверь, потом сделай» и возвращает состояние задач в зеркало. Спеки: `openspec/specs/`; активные change — `openspec/changes/` (кроме `archive/`).
## Стек и ограничения
- Веб-приложение — `web/` (React 19 + TypeScript + Vite, TanStack Query, Vitest, Playwright; Node 24, npm); сборка в `src/jira_hub/web/app/` не в git.
- Python 3.13 от uv (`.python-version`, `tool.uv.python-preference = "only-managed"`: системный Python miniconda несёт SQLite 3.51.1 с взаимной блокировкой, O-14 #29), uv, hatchling, pytest; HTTP на stdlib (`http.server`), БД — SQLite (stdlib), MCP — пакет `mcp` (закреплён `<2`, рулинг R-10).
- Хаб не знает пароль Jira и не ходит в Jira: учётные данные и сеть до Jira только у агента.
- Зеркало задач пишет только `mirror.apply_state` по загрузке состояния от агента (`POST /agent/state`); правки идут командами через очередь.
- Хаб не создаёт команды сам: повтор после failed — новая команда по запросу человека/интерфейса. Команду, выданную агенту и оставшуюся без отчёта, хаб довыдаёт при следующем claim (до 3 выдач, затем failed).
- Данные: `~/.local/share/jira-hub/` (`JIRA_HUB_HOME`): `hub.sqlite` (зеркало, очередь, журнал изменений `issue_change`), `backups/`, `release/` (опубликованная версия агента), `files/` (файлы вложений по sha256, уборка через 30 дней), `downloads/` (копии вложений для MCP `<id>/<имя>`, уборка 30 дней); каталог данных, `backups/`, `release/`, `files/` и `downloads/` — права 0700. Порт по умолчанию 8790.
- Два статических токена (в таблице `setting`): агентский открывает только `/agent/*`, клиентский — остальное.
## Архитектура
- `src/jira_hub/core/` — ядро: `db`, `settings`, `projects`, `queue` (механика очереди), `files`, `api` (HTTP, `CORE_ROUTES` + маршруты модулей в `ROUTES`, фоновый `tick`), `relay`, `release`, `client`, `mcp` (каркас MCP), `cli`; контракт модуля `Module`/`CommandHooks` — `core/modules.py`, реестр — `core/registry.py`.
- `src/jira_hub/modules/jira/` — модуль jira: `schema`, `mirror`, `history`, `attachments`, `fetch`, `commands`, `refresh`, `projects`, `hooks` (смысл типов команд для очереди), `api`, `mcp`, `settings`, `importer`/`cli`. Входная точка — `MODULE` в `modules/jira/__init__.py`.
- Граница: ядро не импортирует модули (кроме ленивого импорта в `registry.py` и шагах миграций `db.py`), типы команд и термины jira живут в модуле, новые имена в ядре — без «jira»; проверяет `tests/test_registry.py`. Места долга проектов (`board_id`, `board_type`, `issues_count`) помечены `долг hub-modules`.
- Цепочка ядра: `db` ← `settings`, `projects` ← `queue` ← `api` ← `cli`; `files` ← `queue`, `api`; реле `relay` — поток в `serve`; `backup` вызывается из `api` и из `db`. Модуль jira: `hooks → commands/mirror/history/refresh/attachments/fetch → core`.
- Веб: оболочка и общее — `web/src/{shell,ui,api,projects,auth,styles}`, модули — `web/src/modules/<имя>/` (контракт `HubModule` в `modules/types.ts`, список — `modules/index.ts`); модуль jira — страницы доски, задач и ленты.
- Подробная карта модулей и границ — `ARCHITECTURE.md`.
## Команды
- `uv run pytest` — весь набор тестов. Зелёным считается только весь набор.
- `uv sync` — установка окружения.
- `uv run jira-hub init --config PATH [--force]` — настройки проекта из `config.example.json`, создание токенов.
- `uv run jira-hub serve [--host] [--port]` — локальный запуск хаба.
- Веб-приложение — `http://127.0.0.1:8790/app/` (доска — `/app/board?project=<метка>`, задачи — `/app/issues?project=<метка>`, лента изменений — `/app/feed?project=<метка>`; вход по клиентскому токену); `/` и `/links` перенаправляют на доску.
- `make web` — сборка приложения (после `git pull` на Mac mini, до перезапуска хаба); `make check` — pytest + lint и тесты фронта; `cd web && npm run dev` — разработка с прокси на тестовый хаб `bash web/scripts/test-hub.sh` (:8791); `cd web && npm run e2e` — Playwright; `cd web && npm run golden` — перегенерировать эталоны доски из `links.html@3931a88` (только при смене фикстур).
- `uv run jira-hub import FOLDER` — импорт CSV-пакета `admin/jira-sync`; `uv run jira-hub mcp [модуль] [--project <метка>]` — MCP модуля по stdio (по умолчанию `jira`; с привязкой сессии к проекту); `uv run jira-hub tokens` — показать токены.
- `uv run jira-hub agent publish PATH` — опубликовать версию агента для самообновления (только вверх).
## Тесты
- Golden-тесты: `tests/fixtures/<name>.*` + эталон `<name>.expected.*`. Внешние сервисы в тестах мокаются.
- SQLite в памяти или во временном `JIRA_HUB_HOME` (`tmp_path`); API поднимается на порту 0 в отдельном потоке. Jira и агент в тестах не участвуют.
- Соединение SQLite в тесте — через фикстуру `open_db` (`tests/conftest.py`) или фикстуру `conn` модуля, иначе с явным `close()`: `pyproject.toml` (`filterwarnings`) делает незакрытое соединение ошибкой набора.
- Фронт: golden-эталоны доски — `web/src/modules/jira/board/__fixtures__/*.expected.json` (их пишет `web/scripts/golden-from-links.ts` из старой страницы); тесты рядом с кодом `*.test.ts(x)`; e2e — `web/e2e/` на тестовом хабе.
## Первая подсистема
Очередь команд (`core/queue.py`, Task 5): пакеты, порядок `(batch.created_at, seq)`, одна команда в полёте, временные ключи `new:<n>`, подтверждение по состоянию зеркала. Остальное (API, MCP, импорт) надстраивается над ней.
## OpenSpec и superpowers
- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
- Когда brainstorming классифицировал задачу как architectural, структуру change создать через `openspec new change <id>` (или `/opsx:propose <id>`), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers.
- `/opsx:apply` не использовать: исполнение идёт через writing-plans и SDD/executing-plans.
- `openspec archive <id>` — после merge PR, как часть чеклиста закрытия.
- Хук require-plan защищает любой каталог `src/` на любой глубине. Если код лежит иначе — перечислить каталоги (префиксы от корня, по одному в строке) в `.claude/code-paths`.