Вложения из Jira по запросу: список при синхронизации, скачивание в вебе и MCP (#83) #84

Merged
aep merged 17 commits from feat/83-attachment-fetch into main 2026-10-22 15:05:52 +03:00
Owner

Closes #83. Агентская часть — aep/jira-agent#43 (PR в jira-agent, агент v14).

Что сделано

  • Схема v6: issue_attachment — список вложений задачи без содержимого, его пишет только apply_state (агент v14 присылает attachments); attachment_file — скачанные файлы.
  • Модуль attachments: ссылки на вложения в описании и комментариях (!имя!, !имя|…!, [^имя], [текст|^имя]), состояние вложения (none / fetching / ready / failed), формат размера.
  • Команда fetch_attachment: пакет из одной команды, без хвостового refresh и без резервной копии; одна команда на вложение (queue.request_fetch в одной транзакции); предел 10 МБ; агент старше v14 — 409.
  • API: POST /agent/files, GET /attachments/{id}, POST /attachments/{id}/fetch, GET /attachments/{id}/content; в GET /issues/{key} — attachments, description_attachment_ids, attachment_ids у комментариев; уборка downloads/.
  • MCP: вложения в issue и comment; инструмент attachment ждёт файл и отдаёт путь к копии с исходным именем <данные хаба>/downloads/<id>/<имя>.
  • Веб: раздел «Вложения (N)» в карточке, плашки файлов под комментарием, ссылки на вложения в тексте — кнопки скачивания; «Загружается…», пока агент везёт файл; больше 10 МБ — ссылка «Открыть в Jira».

Спека — openspec/changes/attachment-fetch/, план — docs/superpowers/plans/2026-10-22-attachment-fetch-hub.md.

Проверка

make check на de07623: pytest 741 passed, vitest 454 passed (44 файла), lint и build чистые. Финальное ревью (opus): I-1 и I-2 исправлены, повторное ревью — новых Critical/Important нет. Живой проверки в браузере не было (vitest на jsdom).

Rulings I made

  • R-1 (design #14): command_id/batch_id в состоянии — живой команды при fetching, иначе последней; ready требует и строку attachment_file, и файл в хранилище.
  • R-2 (design #15): «самое новое» при повторе имени — максимум по (created_at, число id, id).
  • R-3 (design #16): в request_fetch сначала 409 agent_too_old, потом 413.
  • R-4 (design #17): TooLarge наследует CommandError; attachment_id — только цифры; отказы done (нет файла, чужой размер) — один текст.
  • R-5 (design #18): fetch_attachment = 2 с в DEFAULT_PACING и pacing_from_config.
  • R-6 (design #19): возраст каталога в sweep_downloads — по самому новому mtime каталога и файлов; сбой уборки downloads/ не мешает files/.
  • R-7 (design #20): неизвестный id — 404 no_attachment; Content-Type — mime_type только вида тип/подтип, иначе application/octet-stream.
  • R-8 (design #21): safe_name заменяет «.» и «..» на attachment-<id>.
  • R-9 (design #22): MCP опрашивает раз в 2,5 с; «взято самое новое» — только в ответе ready.
  • R-10 (design #23): веб — опрос не стартует до нажатия; «Загружается…» дописывается к имени; предел 10 МБ продублирован во фронте; порядок и дубли — по created с числовым id.
  • R-11 (design #24): TooLarge в POST /batches и в повторе команды — 413 too_large.
  • R-12 (design #25): !имя! — перекрывающийся regex с условием «после ! не пробел».

Решения brainstorming без обсуждения — design.md #1–#13.

Deferred (в follow-up при закрытии)

T2.1 (тай-брейк id и равные created_at), T2.2 (N+1 запросов command в get_issue), T2.4 (тесты fetching поверх failed и ready поверх нового failed), T3.5 (порядок импортов в test_queue.py), T4.4 (тест «ready, но файла нет»), T5.1 (размеры в тестах 239821 вместо 240000), T5.4 (тесты обрезки wait_seconds и повтора), T5.5 (тест пути опроса до ready), T6.2 (мигает устаревшая ошибка после повтора), T6.3 (AuthError/HubDownError при опросе — текст вместо экрана входа), T6.6 (двойной import), T6.8 (тесты бейджа после invalidate и порядка на узком экране), M-4 (веб пересортирует вложения по Date.parse), F.1 (isdigit() принимает Unicode-цифры).

Выкатка

  1. Merge → git pull → make web → перезапуск хаба владельцем → /mcp → reconnect.
  2. Merge PR агента → jira-hub agent publish ../jira-agent/src/agent.py → agent_status показывает v14.
  3. Полный refresh → списки вложений; MCP attachment по скриншоту и Excel; «Скачать» в карточке.

🤖 Generated with Claude Code

Closes #83. Агентская часть — aep/jira-agent#43 (PR в jira-agent, агент v14). ## Что сделано - **Схема v6:** `issue_attachment` — список вложений задачи без содержимого, его пишет только `apply_state` (агент v14 присылает `attachments`); `attachment_file` — скачанные файлы. - **Модуль `attachments`:** ссылки на вложения в описании и комментариях (`!имя!`, `!имя|…!`, `[^имя]`, `[текст|^имя]`), состояние вложения (`none` / `fetching` / `ready` / `failed`), формат размера. - **Команда `fetch_attachment`:** пакет из одной команды, без хвостового `refresh` и без резервной копии; одна команда на вложение (`queue.request_fetch` в одной транзакции); предел 10 МБ; агент старше v14 — 409. - **API:** `POST /agent/files`, `GET /attachments/{id}`, `POST /attachments/{id}/fetch`, `GET /attachments/{id}/content`; в `GET /issues/{key}` — `attachments`, `description_attachment_ids`, `attachment_ids` у комментариев; уборка `downloads/`. - **MCP:** вложения в `issue` и `comment`; инструмент `attachment` ждёт файл и отдаёт путь к копии с исходным именем `<данные хаба>/downloads/<id>/<имя>`. - **Веб:** раздел «Вложения (N)» в карточке, плашки файлов под комментарием, ссылки на вложения в тексте — кнопки скачивания; «Загружается…», пока агент везёт файл; больше 10 МБ — ссылка «Открыть в Jira». Спека — `openspec/changes/attachment-fetch/`, план — `docs/superpowers/plans/2026-10-22-attachment-fetch-hub.md`. ## Проверка `make check` на de07623: pytest 741 passed, vitest 454 passed (44 файла), lint и build чистые. Финальное ревью (opus): I-1 и I-2 исправлены, повторное ревью — новых Critical/Important нет. Живой проверки в браузере не было (vitest на jsdom). ## Rulings I made - R-1 (design #14): `command_id`/`batch_id` в состоянии — живой команды при `fetching`, иначе последней; `ready` требует и строку `attachment_file`, и файл в хранилище. - R-2 (design #15): «самое новое» при повторе имени — максимум по `(created_at, число id, id)`. - R-3 (design #16): в `request_fetch` сначала 409 `agent_too_old`, потом 413. - R-4 (design #17): `TooLarge` наследует `CommandError`; `attachment_id` — только цифры; отказы `done` (нет файла, чужой размер) — один текст. - R-5 (design #18): `fetch_attachment` = 2 с в `DEFAULT_PACING` и `pacing_from_config`. - R-6 (design #19): возраст каталога в `sweep_downloads` — по самому новому mtime каталога и файлов; сбой уборки `downloads/` не мешает `files/`. - R-7 (design #20): неизвестный id — 404 `no_attachment`; `Content-Type` — `mime_type` только вида тип/подтип, иначе `application/octet-stream`. - R-8 (design #21): `safe_name` заменяет «.» и «..» на `attachment-<id>`. - R-9 (design #22): MCP опрашивает раз в 2,5 с; «взято самое новое» — только в ответе `ready`. - R-10 (design #23): веб — опрос не стартует до нажатия; «Загружается…» дописывается к имени; предел 10 МБ продублирован во фронте; порядок и дубли — по `created` с числовым id. - R-11 (design #24): `TooLarge` в `POST /batches` и в повторе команды — 413 `too_large`. - R-12 (design #25): `!имя!` — перекрывающийся regex с условием «после `!` не пробел». Решения brainstorming без обсуждения — design.md #1–#13. ## Deferred (в follow-up при закрытии) T2.1 (тай-брейк id и равные `created_at`), T2.2 (N+1 запросов `command` в `get_issue`), T2.4 (тесты `fetching` поверх `failed` и `ready` поверх нового `failed`), T3.5 (порядок импортов в `test_queue.py`), T4.4 (тест «ready, но файла нет»), T5.1 (размеры в тестах 239821 вместо 240000), T5.4 (тесты обрезки `wait_seconds` и повтора), T5.5 (тест пути опроса до `ready`), T6.2 (мигает устаревшая ошибка после повтора), T6.3 (`AuthError`/`HubDownError` при опросе — текст вместо экрана входа), T6.6 (двойной import), T6.8 (тесты бейджа после invalidate и порядка на узком экране), M-4 (веб пересортирует вложения по `Date.parse`), F.1 (`isdigit()` принимает Unicode-цифры). ## Выкатка 1. Merge → `git pull` → `make web` → перезапуск хаба владельцем → `/mcp` → reconnect. 2. Merge PR агента → `jira-hub agent publish ../jira-agent/src/agent.py` → `agent_status` показывает v14. 3. Полный `refresh` → списки вложений; MCP `attachment` по скриншоту и Excel; «Скачать» в карточке. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 17 commits 2026-10-22 14:24:25 +03:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Модуль attachments (fmt_size, ref_names, resolve, state, get); mirror.get_issue
отдаёт attachments, description_attachment_ids, attachment_ids комментариев
и скрывает fetch_attachment из commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Раздел «Вложения (N)», плашки под комментарием и ссылки !имя!, [^имя] в тексте;
хук useAttachmentDownload: fetch, опрос раз в 2 с, сохранение через Blob (api.blob).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
I-1 I-2 M-1 M-2 M-3 M-5 M-6 M-7 T3.4 T4.3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep merged commit 74050e4179 into main 2026-10-22 15:05:52 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: aep/sdlc-hub#84
No description provided.