Andrey Epifancev 54d87225bd OpenSpec: change attachment-fetch — вложения из Jira по запросу (#83)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:59:37 +04:00

181 lines
21 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.

## MODIFIED Requirements
### Requirement: Карточка задачи
Адрес `/app/issues/{key}` SHALL открывать карточку задачи из `GET /issues/{key}` модальным окном поверх страницы, с
которой её открыли (список «Задачи» или доска); страница под окном SHALL оставаться смонтированной и сохранять фильтры,
загруженные строки, прокрутку и раскладку. Перезагрузка страницы с открытым окном SHALL сохранять страницу под окном. Открытый напрямую
адрес (новая вкладка, закладка) SHALL
показывать окно поверх списка задач без фильтров. Окно SHALL закрываться кнопкой ✕, клавишей Esc и щелчком по фону (нажатие и отпускание — на фоне; выделение текста,
отпущенное за краем окна, окно не закрывает) и
возвращать на страницу, с которой его открыли, даже после переходов по связям внутри окна; «назад» браузера после
закрытия MUST NOT открывать карточку снова. Переход по связанной задаче SHALL менять содержимое окна и адрес, «назад»
браузера — возвращать к прежней карточке. Пока окно открыто, фокус SHALL оставаться в окне, страница под ним не
прокручивается.
Содержимое окна:
- шапка: ключ, статус Jira, «Открыть в Jira», ✕, тема; строка
«исполнитель · срок · спринт · эпик» без пустых частей, срок — `planned_end`, иначе `duedate`, с пометкой «просрочено»,
если он раньше сегодняшней даты и статус Jira не входит в `closed_statuses`; эпик — темой
(`epic_summary`), иначе ключом;
- «Связи» по группам в порядке: «Зависит от» (входящие связи логических типов `блокирует начало` и `блокирует
завершение` из `link_types`, с меткой «начало» / «завершение»), «Блокирует» (исходящие тех же типов, с теми же метками),
«Связана с» (тип `связанные` в обе стороны), «Прочие» (тип Jira вне `link_types`: имя типа и направление «→» / «←»);
в строке — ключ (открывает связанную задачу в окне), тема и статус Jira, а если задачи нет в зеркале — ключ и «нет в
зеркале»; пустые группы не показываются, без связей — «связей нет»;
- «Описание» — текст, отрендеренный по требованию «Разметка Jira в карточке», с кнопкой «исходник» / «текст» в
заголовке раздела; длиннее 8 строк — свёрнуто с кнопкой «развернуть» / «свернуть» в обоих режимах; без описания
раздела нет;
- «Вложения (N)» — новые сверху: значок по типу файла (картинка, таблица, PDF, прочее), имя, размер, «автор · дата» и
кнопка скачивания (требование «Скачивание вложений в карточке»); без вложений раздела нет;
- «Комментарии (N)» — новые сверху, автор, время, кнопка «исходник» / «текст» у каждого комментария, текст,
отрендеренный по требованию «Разметка Jira в карточке», под текстом — вложения, на которые ссылается комментарий
(`attachment_ids`): строка «плашек» с именем и размером, щелчок — скачивание; кнопка «Скрыть» / «Показать» сворачивает раздел, выбор SHALL
запоминаться в браузере для всех карточек; без комментариев — «комментариев нет»;
- «Служебные заметки» (требование «Служебные заметки»);
- «Прочие поля» — непустые из: тип, приоритет, автор, метки, плановое начало, обновлена в Jira, сосед по связям;
- «Команды моста (N)» — 10 последних команд по задаче (id, пакет, тип, статус, время завершения, ошибка), раздел свёрнут
при открытии; если команд не было — текст «команд по задаче не было».
На ширине от 900 px «Связи», «Описание», «Вложения», «Комментарии» — в левой колонке, заметки, прочие поля и команды — в правой;
уже — одна колонка: заметки, связи, описание, вложения, комментарии, прочие поля, команды. Если задачи нет в зеркале (404), окно
SHALL сообщать об этом с кнопкой «Закрыть»; хаб не отвечает — сообщение и «Повторить», ранее загруженные данные
остаются.
#### Scenario: Связанная задача
- **WHEN** у AURORA-1620 исходящая связь `ASUPR_Блокер_Начала` на AURORA-1621 «Расчёт свода» в статусе «В работе»
- **THEN** в карточке в группе «Блокирует» строка «начало · AURORA-1621 · Расчёт свода · В работе»; щелчок по ключу
меняет содержимое окна на AURORA-1621, адрес — `/app/issues/AURORA-1621`; «назад» браузера возвращает AURORA-1620
#### Scenario: Входящий блокер
- **WHEN** у AURORA-1620 входящая связь `ASUPR_Блокер_Завершения` от AURORA-1500
- **THEN** строка с AURORA-1500 и меткой «завершение» — в группе «Зависит от»
#### Scenario: Задача без команд
- **WHEN** по AURORA-1620 не было команд
- **THEN** в разделе «Команды моста» — текст «команд по задаче не было», таблицы нет
#### Scenario: Закрытие после переходов
- **WHEN** со списка «Задачи» открыта AURORA-1620, в окне — переход по связи на AURORA-1621, нажата Esc
- **THEN** окно закрыто, адрес — адрес списка с прежними фильтрами; «назад» браузера не открывает карточку
#### Scenario: Прямой адрес
- **WHEN** в новой вкладке открыт `/app/issues/AURORA-1620`, затем нажат ✕
- **THEN** под окном был список задач без фильтров; после закрытия адрес `/app/issues`, «назад» не открывает карточку
#### Scenario: Скрытые комментарии
- **WHEN** в карточке нажато «Скрыть» у комментариев, затем страница перезагружена и открыта другая карточка
- **THEN** комментарии в ней скрыты, видна кнопка «Показать» и число комментариев
#### Scenario: Просрочка закрытой задачи
- **WHEN** у AURORA-1620 срок 2026-10-01, сегодня 2026-10-17, статус Jira «Готово»
- **THEN** в строке под темой «срок 01.10» без пометки «просрочено»; при статусе «В работе» — с пометкой
### Requirement: Разметка Jira в карточке
Описание задачи и текст каждого комментария в карточке SHALL показываться отрендеренной вики-разметкой Jira (хаб
хранит их как есть, в wiki) так, как их показывает Jira. Разбираются:
- блоки: заголовки `h1.`–`h6.` в начале строки; списки — строки, начинающиеся (после необязательных пробелов) с
маркеров `*`, `#` и пробела (уровень вложенности — числом маркеров, `*` — маркированный, `#` — нумерованный,
смешанная вложенность `*#` — по последнему маркеру) или с одного `-` и пробела (маркированный, первый уровень);
`--`, `*-` и прочие сочетания с `-` — не список; таблицы — строки, начинающиеся с
`|`, ячейки `||заголовок||` и `|ячейка|`; `{code}` / `{code:язык}` … `{code}` и `{noformat}` … `{noformat}` —
моноширинный блок без разбора разметки внутри; `{quote}` … `{quote}` и строка `bq. ` — цитата; открывающий тег
блока — в начале строки, закрывающий — в любом месте той же или последующей строки, текст после закрывающего тега
разбирается дальше как обычные строки; тег не в начале строки — текст как есть; строка `----` —
горизонтальная черта; прочие строки — абзацы, разделённые пустыми строками, одиночный перенос внутри абзаца —
перенос строки;
- внутри строки: `*жирный*`, `_курсив_`, `-зачёркнутый-`, `+подчёркнутый+`, `^верхний^`, `~нижний~`, `{{моно}}`
(`{{…}}` — без разбора внутри); маркер выделения действует, только если открывающий стоит в начале текста или после
пробела либо знака препинания и за ним не пробел и не тот же символ (`**x**` — текст как есть), а закрывающий стоит после не-пробела и за ним не буква и не
цифра (`data_type`, `2026-10-15`, `счёт-фактура`, `a*b*c` — обычный текст); `\\` — перенос строки; `\` перед
символом разметки — сам символ;
- ссылки: `[текст|адрес]`, `[адрес]` и голые адреса `http://`, `https://` — ссылка в новой вкладке, только для схем
`http`, `https`, `mailto`, иначе текст как есть; `[ключ задачи]`, `[текст|ключ задачи]` и ключ задачи в тексте вида
`ПРОЕКТ-123`, где `ПРОЕКТ` — `project_key` из настроек хаба, — ссылка, открывающая карточку этой задачи в окне
(`UTF-8`, `SHA-256` и ключи других проектов — текст);
`[~логин]` — «@логин»;
- ссылки на вложения задачи — `!имя!`, `!имя|параметры!`, `[^имя]`, `[текст|^имя]` с тем же разбором имени, что у хаба
(`hub-api`, «Ссылки на вложения в тексте»), имя есть среди вложений задачи: ссылка-кнопка со значком скрепки и текстом
— именем файла (у `[текст|^имя]` — текстом), щелчок — скачивание; картинка в тексте не показывается; имени нет среди
вложений — исходник как есть.
Всё, что разборщик не распознал (`{panel}`, `{color}`, `!картинка!` и `[^имя]` без такого вложения у задачи, незакрытые маркеры и блоки и прочее), SHALL
показываться как текст исходника без потерь. Разметка MUST NOT превращаться в HTML строкой (`innerHTML`,
`dangerouslySetInnerHTML`): текст задачи не может добавить на страницу элементы, атрибуты или скрипты. Если рендер
блока завершился ошибкой, блок SHALL показываться исходником.
Кнопка «исходник» у описания и у каждого комментария SHALL переключать этот блок на исходник — текст с сохранёнными
переносами строк, как без разбора разметки, — и обратно кнопкой «текст»; остальные блоки не переключаются. При каждом
открытии карточки (в том числе при переходе по связи) все блоки показываются отрендеренными, выбор не запоминается.
#### Scenario: Описание со структурой
- **WHEN** описание AURORA-1620 — `h3. Контекст`, строка `Шаги:`, затем `# Загрузить ОСВ` и `# Открыть документ`,
затем пустая строка и `*Итог:* ошибка в {{data_type}}`
- **THEN** в «Описании» заголовок «Контекст», абзац «Шаги:», нумерованный список из двух пунктов и абзац, где «Итог:» —
жирным, «data_type» — моноширинным; символов `h3.`, `#`, `*`, `{{` не видно
#### Scenario: Переключение на исходник
- **WHEN** в карточке AURORA-1620 у описания нажато «исходник»
- **THEN** описание показано исходником с переносами строк (видны `h3. Контекст` и `# Загрузить ОСВ`), кнопка — «текст»;
комментарии остаются отрендеренными; после «текст» описание снова отрендерено
#### Scenario: Исходник у одного комментария
- **WHEN** у второго комментария нажато «исходник», затем открыта связанная задача и возврат «назад»
- **THEN** сразу после нажатия исходником показан только второй комментарий; после возврата все комментарии
отрендерены
#### Scenario: Ключ задачи в тексте
- **WHEN** комментарий — `П. 1 ведётся в AURORA-1445; AURORA-1645 снята как дубль.`
- **THEN** «AURORA-1445» и «AURORA-1645» — ссылки; щелчок по первой меняет содержимое окна на AURORA-1445
#### Scenario: Неизвестная разметка и опасный адрес
- **WHEN** описание — `{color:red}важно{color}`, `!схема.png!` и `[нажми|javascript:alert(1)]`
- **THEN** все три показаны текстом исходника как есть, ссылки и картинки нет, скрипт не выполняется
#### Scenario: Списки с отступом и пустыми строками
- **WHEN** описание — `Шаги воспроизведения:`, ` * Загрузить ОСВ.`, пустая строка, ` * Дождаться обработки.`
- **THEN** абзац «Шаги воспроизведения:» и маркированные пункты «Загрузить ОСВ.» и «Дождаться обработки.» без
звёздочек
#### Scenario: Обычный текст не портится
- **WHEN** описание — `Поле data_type = null, дата 2026-10-15, оборот 18 803 903,65 - 9 351 102,89, a*b*c, UTF-8,
**важно**`
- **THEN** текст показан как есть, без курсива, зачёркивания, жирного и ссылок
#### Scenario: Скриншот в тексте комментария
- **WHEN** комментарий — «см. !скрин 1.png|thumbnail!», у задачи есть вложение «скрин 1.png»
- **THEN** вместо `!скрин 1.png|thumbnail!` — кнопка со скрепкой «скрин 1.png»; под комментарием плашка «скрин 1.png ·
234,4 КБ»; щелчок по любой из них скачивает файл
## ADDED Requirements
### Requirement: Скачивание вложений в карточке
Кнопка скачивания вложения (в разделе «Вложения», плашка под комментарием, ссылка в тексте) SHALL работать одинаково:
1. `POST /attachments/{id}/fetch {author: "web"}`;
2. `state = ready` — `GET /attachments/{id}/content` с клиентским токеном, браузер сохраняет файл под именем `filename`
(через `Blob` и временную ссылку `download`; адрес с токеном в строке браузера MUST NOT появляться);
3. `state = fetching` — у всех кнопок этого вложения в карточке вместо «Скачать» — «Загружается…» (неактивна), опрос
`GET /attachments/{id}` раз в 2 с, пока окно открыто на этой задаче; `ready` — шаг 2 сам, без второго нажатия; закрыли
окно или ушли на другую задачу — опрос прекращается, команда остаётся в очереди (файл ляжет в хаб);
4. `failed` — под вложением в разделе «Вложения» текст ошибки и кнопка «Повторить» (новый запрос); 409 `agent_too_old`
и прочие ошибки хаба — их текст там же.
Вложение с `state = ready` в разделе «Вложения» помечается «у хаба» и скачивается сразу. Размер неизвестен — «размер неизвестен» и обычная кнопка.
Вложение больше 10 МБ —
вместо кнопки ссылка «Открыть в Jira» (`<адрес Jira>/secure/attachment/<id>/<имя в URL-кодировке>`, новая вкладка) с
подсказкой «больше 10 МБ — только в Jira»; плашка и ссылка в тексте ведут туда же.
#### Scenario: Скачать скриншот
- **WHEN** в карточке AURORA-1650 нажато «Скачать» у «скрин 1.png», файла у хаба нет, агент онлайн
- **THEN** кнопка — «Загружается…», через несколько секунд браузер сохраняет «скрин 1.png», кнопка снова «Скачать», у
вложения отметка «у хаба»
#### Scenario: Окно закрыли во время скачивания
- **WHEN** нажато «Скачать», затем окно карточки закрыто
- **THEN** опросов `GET /attachments/{id}` больше нет; при следующем открытии карточки, если файл уже у хаба, вложение с
отметкой «у хаба»
#### Scenario: Большой файл
- **WHEN** у вложения «запись.mp4» размер 25 МБ
- **THEN** вместо «Скачать» — ссылка «Открыть в Jira» с подсказкой «больше 10 МБ — только в Jira»
#### Scenario: Агент старой версии
- **WHEN** нажато «Скачать», хаб ответил 409 «агент v13 не умеет скачивать вложения — нужен v14»
- **THEN** под вложением этот текст и «Повторить»