289 lines
14 KiB
Markdown
289 lines
14 KiB
Markdown
# Этапы разработки
|
||
|
||
## Общий принцип
|
||
|
||
Не делаем сразу тяжелую доменную платформу.
|
||
|
||
Идем вертикальными поставками:
|
||
1. сначала транспорт и создание задачи
|
||
2. потом source-side представление
|
||
3. потом синхронизация
|
||
4. потом уведомления и полировка
|
||
|
||
## Этап 0. Термины и навигация
|
||
|
||
### Цель
|
||
|
||
Подготовить интерфейс и словарь сущностей без ломки backend-логики.
|
||
|
||
### Что входит
|
||
|
||
- `Рабочие элементы` -> `Внутренний контур`
|
||
- новая вкладка `Внешние контуры`
|
||
- подготовка i18n-ключей и текстов
|
||
- фиксация терминов в продуктовой документации
|
||
|
||
### Результат
|
||
|
||
В проекте уже есть новая структура навигации, но бизнес-логика еще не внедрена.
|
||
|
||
## Этап 1. Маршрутизация запроса в целевой проект
|
||
|
||
### Цель
|
||
|
||
Сделать рабочий сценарий отправки задачи из source project в target project.
|
||
|
||
### Что входит
|
||
|
||
- новая create-form во `Внешних контурах`
|
||
- выбор целевого проекта
|
||
- выбор исполнителя из целевого проекта
|
||
- orchestration endpoint
|
||
- использование intake bridge как backend-моста
|
||
- немедленный перевод в default-state целевого проекта
|
||
- создание target issue
|
||
- запись source/target metadata
|
||
|
||
### Что не входит
|
||
|
||
- полноценная синхронизация файлов
|
||
- полноценная зеркальная история
|
||
- сложные уведомления
|
||
|
||
### Критерий приемки
|
||
|
||
- пользователь отправляет внешний запрос
|
||
- в целевом проекте появляется обычная задача
|
||
- задача не зависает в triage
|
||
- source-side список уже видит факт отправки
|
||
|
||
### Статус
|
||
|
||
Реализовано.
|
||
|
||
Что работает фактически:
|
||
- `POST /external-contours/` создает target issue в целевом проекте
|
||
- target issue сразу попадает в обычный workflow целевого проекта
|
||
- source-side `GET /external-contours/` возвращает отправленные запросы с metadata источника и цели
|
||
- target contour теперь выбирается по policy `same workspace + intake enabled`, а не только из joined projects
|
||
- прямое membership в target project для отправки не требуется
|
||
|
||
## Этап 2. Source-side список и статусная пришлепка
|
||
|
||
### Цель
|
||
|
||
Дать инициатору читаемый контроль за отправленными запросами.
|
||
|
||
### Что входит
|
||
|
||
- список `Открытые`
|
||
- список `Завершенные`
|
||
- текущий статус задачи как пришлепка
|
||
- отображение целевого проекта
|
||
- отображение исполнителя
|
||
- отображение даты обновления
|
||
- индикатор новых изменений
|
||
|
||
### Критерий приемки
|
||
|
||
- в проекте-источнике виден список отправленных запросов
|
||
- у каждой записи отображается актуальный статус целевой задачи
|
||
|
||
### Статус
|
||
|
||
Реализовано частично в рамках текущего вертикального среза.
|
||
|
||
Что уже работает:
|
||
- source-side список `Открытые / Завершенные`
|
||
- status pill по фактическому state целевой задачи
|
||
- отображение целевого проекта
|
||
- отображение исполнителей целевого контура
|
||
- отображение фактической даты последнего изменения
|
||
- индикатор новых изменений в source-side списке на базе unread уведомлений
|
||
- открытие source-side detail экрана
|
||
|
||
Что еще остается на следующие этапы:
|
||
- полноценная зеркальная activity/history
|
||
- уведомления
|
||
|
||
Дополнительно реализовано:
|
||
- source-side detail показывает блок маршрутизации
|
||
- в карточке видны источник, цель, отправитель, дата отправки и связанная целевая задача
|
||
|
||
## Этап 3. Source-side детальный экран и зеркалирование изменений
|
||
|
||
### Цель
|
||
|
||
Сделать так, чтобы отправитель видел ход исполнения не только по одной плашке статуса.
|
||
|
||
### Что входит
|
||
|
||
- детальный экран внешнего контура
|
||
- блок `Исходный запрос`
|
||
- блок `Текущий статус`
|
||
- mirrored activity stream
|
||
- mirrored comments
|
||
- mirrored files
|
||
- mirrored description updates
|
||
|
||
### Важное правило
|
||
|
||
Исходный запрос не должен теряться.
|
||
|
||
Поэтому target-изменения лучше показывать отдельным блоком истории, а не просто затирать исходное описание.
|
||
|
||
### Критерий приемки
|
||
|
||
- если в target issue добавили файл, инициатор видит его в source-side карточке
|
||
- если в target issue поменяли статус, это видно в source-side карточке
|
||
- если в target issue написали комментарий, это видно в source-side карточке
|
||
|
||
### Статус
|
||
|
||
Реализовано частично.
|
||
|
||
Что уже работает:
|
||
- source-side detail использует отдельный экран `Внешних контуров`
|
||
- в карточке отображается блок маршрутизации с ключевой source-target связью
|
||
- у отправителя открытый внешний запрос редактируется прямо из source-side карточки по полям `заголовок` и `описание`, даже без membership в target project
|
||
- текущий статус берется из фактического state целевой задачи
|
||
- если у инициатора нет membership в target project, карточка переключается в source-side readonly режим без прямого открытия чужого проекта
|
||
- блок `Маршрутизация` перестроен в фиксированный формат `3 x 3`
|
||
- в `Маршрутизацию` перенесены `Назначенный` и `Срок выполнения`
|
||
- в блоке `Свойства` убраны дубли `Внешний контур`, `Назначенный` и `Срок выполнения`
|
||
- название исходного внутреннего контура в карточке маршрутизации берется из живого проекта, а не из застывшего metadata snapshot
|
||
- для закрытого внешнего запроса доступны source-side действия `Принять` и `Отклонить`
|
||
- `Принять` фиксирует решение источника в bridge metadata и помечает запрос как принятый во внутренний контур
|
||
- `Отклонить` требует комментарий причины, возвращает target issue в default-state целевого проекта и переносит source-side карточку обратно в список `Открытые`
|
||
- source-side readonly карточка зеркалит актуальные комментарии, вложения и activity целевой задачи
|
||
- вложения доступны через proxy download endpoint без прямого membership в target project
|
||
- detail карточка source-only пользователя обновляется polling-ом и подтягивает новые комментарии без ручной перезагрузки
|
||
- инициатор может отправить комментарий обратно во внешний контур прямо из source-side карточки
|
||
|
||
Что остается:
|
||
- зеркалирование inline-файлов из комментариев и описания, а не только issue attachments
|
||
- realtime вместо polling
|
||
- отдельная сущность или шаг для переноса принятого результата во `Внутренний контур`
|
||
|
||
## Этап 4. Уведомления
|
||
|
||
### Цель
|
||
|
||
Не заставлять инициатора вручную проверять изменения.
|
||
|
||
### Что входит
|
||
|
||
- in-app notification на:
|
||
- принятие
|
||
- перевод статуса
|
||
- добавление файла
|
||
- новый комментарий
|
||
- завершение
|
||
- отмену
|
||
|
||
### Критерий приемки
|
||
|
||
- инициатор получает уведомления по ключевым событиям жизненного цикла внешнего запроса
|
||
|
||
### Статус
|
||
|
||
Реализовано частично.
|
||
|
||
Что уже работает:
|
||
- создаются in-app уведомления по изменениям целевой задачи внешнего контура
|
||
- покрыты события:
|
||
- смена статуса
|
||
- новый комментарий
|
||
- изменение описания
|
||
- новое вложение
|
||
- уведомление привязано к source project, а не требует membership в target project
|
||
- notification payload несет `external contour request id` и `target issue id`
|
||
- список уведомлений помечает такие записи как `is_external_contour = true`
|
||
- notification preview может открыть source-side карточку внешнего контура, а не обычный issue preview
|
||
- открытие source-side карточки помечает связанные unread уведомления как прочитанные и снимает индикатор новых изменений в списке
|
||
|
||
Что остается:
|
||
- in-app уведомления на явные source-side решения `Принять / Отклонить`
|
||
- отдельный индикатор новых изменений в списке `Открытые / Завершенные`
|
||
- push/realtime канал вместо обычного цикла обновления UI
|
||
|
||
## Этап 5. Полировка и правила эксплуатации
|
||
|
||
### Что входит
|
||
|
||
- edge cases
|
||
- повторная отправка
|
||
- отмена
|
||
- защита от удаления target issue
|
||
- аудит прав
|
||
- тест-кейсы
|
||
- регламент эксплуатации
|
||
|
||
## Технические решения, которые желательно держать с самого начала
|
||
|
||
### 1. Не ломать штатный intake
|
||
|
||
Он остается отдельным продуктовым модулем.
|
||
|
||
### 2. Явно хранить source/target связь
|
||
|
||
Даже если первая версия идет через `IntakeIssue.extra`, связь не должна быть неявной.
|
||
|
||
### 3. Использовать фактический статус target issue
|
||
|
||
В source-side плашке лучше показывать не abstract intake status, а реальный текущий статус исполнения.
|
||
|
||
### 4. Не пытаться делать полную двустороннюю редакцию сразу
|
||
|
||
На старте безопаснее сделать:
|
||
- создание из источника
|
||
- исполнение в цели
|
||
- синхронизацию результата обратно в источник
|
||
|
||
## Открытые вопросы
|
||
|
||
### 1. Source-side сущность
|
||
|
||
Нужно принять решение:
|
||
- достаточно ли source-side проекции
|
||
- или нужна отдельная таблица/модель для внешних контуров
|
||
|
||
### 2. Файлы
|
||
|
||
Нужно решить:
|
||
- показываем ли мы source-side ссылку на target asset
|
||
- или физически копируем файл в source representation
|
||
|
||
### 3. Комментарии
|
||
|
||
Нужно решить:
|
||
- комментарии зеркалируются односторонне из цели в источник
|
||
- или источник тоже может отвечать прямо из source-side карточки
|
||
|
||
### 4. Уровень realtime
|
||
|
||
Нужно решить:
|
||
- хватит ли near-realtime через polling и existing refresh
|
||
- или сразу нужен realtime через live-события
|
||
|
||
### 5. Доступ к target issue
|
||
|
||
Нужно решить:
|
||
- должен ли инициатор иметь прямую ссылку на target issue
|
||
- или доступ к target issue должен быть скрыт, а source-side карточка должна быть единственной точкой просмотра
|
||
|
||
Текущее решение:
|
||
- при отсутствии membership в target project прямой переход в target issue скрывается
|
||
- карточка остается доступной из source project
|
||
|
||
## Рекомендуемый порядок фактической разработки
|
||
|
||
1. Этап 0
|
||
2. Этап 1
|
||
3. Этап 2
|
||
4. Этап 3
|
||
5. Этап 4
|
||
6. Этап 5
|
||
|
||
Это даст быстрый полезный результат и не загонит проект в ранний тяжелый refactor.
|