2026-10-14 16:00:57 +04:00

10 KiB
Raw Blame History

Context

Просмотрщик «Спринт 2 линк» — один HTML-файл (~117 КБ, из них ~100 КБ — встроенная выгрузка CSV от 11.10.2026) без зависимостей: разбор CSV фильтра Jira (fromJiraCsv) → модель {nodes, links, sprints} → доска (дорожки по эпикам, карточки, стрелки блокировок, режим «по датам», критический путь, панель проблем, таблица), раскладка и фильтры — в localStorage под ключами graph-<имя>:board, не зависящими от источника; экспорт/импорт JSON (arm-ka-links/1). Модель узла: {key, title, kind, prio, due, status, epic, sprint, who}; связи {src, dst, type} с логическими типами «блокирует начало / блокирует завершение / связанные» (LINK_TYPES переводит имена Jira ASUPR_*).

Хаб (main fc732eb): зеркало issue содержит все нужные поля (planned_end, epic_key, labels, sprint, assignee, issue_type), связи — issue_link (source_key, target_key, link_type) с семантикой Jira, заметки — issue_note, журнал синхронизаций — sync_run. HTTP — http.server, маршруты ROUTES с областью токена, ответы только JSON (_send). GET /issues постраничный и без связей.

Goals / Non-Goals

Goals: доска на живых данных хаба на Mac mini без ручной выгрузки; обновление из Jira кнопкой; перенос раскладки старой страницы; API, пригодный будущему React-приложению.

Non-Goals: запись из доски (viewer-edit), React-приложение, доступ из сети, изменение логики доски.

Decisions

  1. Одна страница — статика хаба. Файл src/jira_hub/web/links.html входит в пакет (hatch включает файлы каталога пакета), читается при запросе через Path(__file__).parent / "web" / "links.html". Маршруты GET / и GET /links без области токена (в ROUTES — область None, _auth не вызывается); ответ — text/html; charset=utf-8, Cache-Control: no-cache (страница меняется вместе с хабом). В api — отправка не-JSON ответа (обработчик возвращает Raw(content_type, bytes)).
  2. GET /board — сырой снимок, а не модель доски. Перевод в модель (вид работы по меткам kind-*, имена эпиков, логические типы связей) остаётся на странице — там он уже есть для CSV, и React-приложению нужен тот же нейтральный формат. Чтение — в одной транзакции (BEGIN на чтение: задачи, связи и last_sync из одного момента). Функция mirror.board(conn) -> dict; api только оборачивает.
  3. last_sync — последний завершённый полный прогон: sync_run с kind = 'jql', query = текущая base_jql, finished_at не пуст, по started_at последний. Обновления по ключам (хвостовые refresh пакетов) в «данные на» не считаются: они обновляют часть задач.
  4. Вход по токену в браузере. Токен — в localStorage["jira-hub-token"], заголовок Authorization: Bearer в каждом fetch к тому же хосту; 401 → форма токена с пояснением. Токен не попадает в HTML, URL и логи хаба.
  5. «Обновить из Jira» через существующие маршруты. GET /agent (офлайн → предупреждение, пакет всё равно ставится), POST /refresh {full: true, author: "viewer"}, опрос GET /batches/{id} каждые 3 с до терминального статуса пакета, затем GET /board. Кнопка недоступна, пока пакет идёт; перезагрузка страницы во время пакета не теряет его — пакет выполнится, «Перечитать» покажет результат.
  6. Модель доски из снимка (fromHub). Эпики (issue_type «Фича»/Epic) — имена дорожек (epic_key → summary), сами карточками не становятся; остальные задачи, включая соседей, — карточки; вид работы — первая метка из KIND_LABELS, иначе issue_type; срок — planned_end, иначе duedate; связи — только типы из LINK_TYPES, src = source, dst = target; ключи из связей, которых нет в зеркале, — заглушки на дорожке «Нет в зеркале». Имя источника — «Хаб».
  7. Импорт JSON в режиме хаба сохраняет раскладку, дорожки и фильтры (как сейчас) и вместо показа снимка из JSON перечитывает GET /board. Загрузка CSV — снимок файла с пометкой «из файла»; «Перечитать» возвращает данные хаба.
  8. Встроенная выгрузка удаляется (DEFAULT_CSV, DEFAULT_NAME и их использование в migrateStore): без хаба страница показывает причину и предлагает загрузить файл. Константа EPICS остаётся запасным словарём имён для CSV.
  9. Проверка страницы — сценарий в браузере. Тестовой обвязки для JS в хабе нет; страница проверяется прогоном через Playwright на хабе с тестовой базой (JIRA_HUB_HOME во временном каталоге, зеркало заполнено через POST /agent/state) и затем на живом хабе: вход, доска, «Обновить из Jira», «Перечитать», импорт JSON, неверный токен, остановленный хаб. Сторона хаба — pytest.

Risks / Trade-offs

  • [Снимок всего зеркала на каждый запрос] → ~200 задач и сотни связей — десятки КБ; при росте — фильтр по спринту в параметрах (не сейчас).
  • [Токен в localStorage доступен скриптам страницы] → страница своя, сторонних скриптов нет; доступ только с Mac mini.
  • [Расхождение KIND_LABELS/LINK_TYPES страницы с настройками хаба (kind_labels, link_types)] → словари совпадают на 14.10.2026; синхронизация из /settings — в React-приложении.
  • [Страница отдаётся без токена] → в ней нет данных; все данные — через API с токеном.

Migration Plan

  1. Merge → git pull в jira-hub → launchctl kickstart -k gui/$(id -u)/ru.aepif.jira-hub.
  2. Открыть http://127.0.0.1:8790/, ввести клиентский токен (uv run jira-hub tokens).
  3. В старой странице «Спринт 2 линк» — «экспорт JSON», в новой — загрузить этот JSON: раскладка применится к живым данным.
  4. Откат: предыдущий main и перезапуск; старая страница в admin/ работает как раньше.

Open Questions

Нет.

Decisions made without discussion

Маркер Дата Решение Обоснование Цена ошибки
R-1 (design #2) 2026-10-14 GET /board отдаёт сырые данные зеркала, перевод в модель доски — на странице нейтральный формат нужен React-приложению; разбор уже есть на странице лишний перевод на клиенте — дёшево переделать
R-2 (design #3) 2026-10-14 «Данные на» — время последнего полного прогона, обновления по ключам не учитываются частичное обновление не делает всю доску свежей владелец может видеть время старше последних изменений по отдельным задачам
R-3 (design #5) 2026-10-14 Пакет обновления — автор viewer, опрос каждые 3 с автор отличает пакеты доски от claude/hub; 3 с — ощутимо и не грузит хаб нет
R-4 (design #6) 2026-10-14 Соседи по связям — обычные карточки, заглушки только для ключей вне зеркала у соседей в зеркале полные данные соседей без спринта скрывает фильтр спринта, как и раньше
R-5 (design #8) 2026-10-14 Встроенная выгрузка от 11.10.2026 удаляется устаревшие данные вводят в заблуждение; файл худеет со ~117 до ~17 КБ без хаба доска пуста до загрузки файла