Add docs for cross-project task routing
This commit is contained in:
parent
39d9d9cd7a
commit
75ba36a318
|
|
@ -0,0 +1,357 @@
|
||||||
|
# Межпроектная маршрутизация задач
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Добавить в `NODE.DC TM` отдельный сценарий межпроектной постановки задач между контурами без перепрошивки штатного `Intake`.
|
||||||
|
|
||||||
|
Базовая идея:
|
||||||
|
- `Рабочие элементы` переименовываются в `Внутренние контуры`
|
||||||
|
- рядом появляется новый модуль `Внешние контуры`
|
||||||
|
- пользователь из проекта-источника создает внешний запрос
|
||||||
|
- запрос уходит в целевой проект
|
||||||
|
- в целевом проекте он становится обычной задачей
|
||||||
|
- в проекте-источнике сохраняется видимый объект маршрутизации со статусом, историей и материалами
|
||||||
|
|
||||||
|
## Почему выбран именно этот путь
|
||||||
|
|
||||||
|
Не трогаем штатный `Intake` как пользовательский модуль.
|
||||||
|
|
||||||
|
Используем его только как внутренний backend-мост, потому что:
|
||||||
|
- механизм уже умеет создавать bridge между `Issue` и `IntakeIssue`
|
||||||
|
- уже есть переход из `TRIAGE` в default-state проекта
|
||||||
|
- уже есть `extra JSON` для метаданных маршрутизации
|
||||||
|
- это самый безопасный путь для форка и дальнейших обновлений
|
||||||
|
|
||||||
|
## Термины
|
||||||
|
|
||||||
|
### Внутренние контуры
|
||||||
|
|
||||||
|
То, что сейчас в UI является обычными рабочими элементами проекта.
|
||||||
|
|
||||||
|
### Внешние контуры
|
||||||
|
|
||||||
|
Новый отдельный модуль проекта, через который задача отправляется в другой проект внутри того же workspace.
|
||||||
|
|
||||||
|
### Проект-источник
|
||||||
|
|
||||||
|
Проект, из которого инициируется запрос.
|
||||||
|
|
||||||
|
### Целевой проект
|
||||||
|
|
||||||
|
Проект, в котором задача реально исполняется.
|
||||||
|
|
||||||
|
### Исходная карточка внешнего контура
|
||||||
|
|
||||||
|
Карточка или запись в проекте-источнике, где инициатор видит:
|
||||||
|
- текущий статус
|
||||||
|
- целевой проект
|
||||||
|
- назначенного исполнителя
|
||||||
|
- историю обработки
|
||||||
|
- файлы
|
||||||
|
- уведомления о ходе работы
|
||||||
|
|
||||||
|
### Целевая задача
|
||||||
|
|
||||||
|
Обычная задача в целевом проекте, которая попадает в обычный workflow этого проекта.
|
||||||
|
|
||||||
|
## Ключевой продуктовый результат
|
||||||
|
|
||||||
|
Пользователь из `Менеджеры` должен иметь возможность:
|
||||||
|
- открыть `Внешние контуры`
|
||||||
|
- выбрать целевой проект, например `Бухгалтерия`
|
||||||
|
- описать запрос
|
||||||
|
- назначить исполнителя из `Бухгалтерия`
|
||||||
|
- отправить запрос
|
||||||
|
|
||||||
|
После этого:
|
||||||
|
- у `Бухгалтерия` появляется обычная задача в их проекте
|
||||||
|
- у отправителя в `Менеджеры` появляется запись во `Внешних контурах`
|
||||||
|
- у этой записи есть статусная пришлепка
|
||||||
|
- при любом изменении статуса в целевом проекте статус в источнике обновляется
|
||||||
|
- если в целевом проекте меняют описание, прикладывают файлы, комментируют или закрывают задачу, это отражается в источнике
|
||||||
|
- инициатор получает уведомления о существенных изменениях
|
||||||
|
|
||||||
|
## Обязательные требования
|
||||||
|
|
||||||
|
### 1. Отдельный модуль
|
||||||
|
|
||||||
|
`Внешние контуры` не должны быть переименованным `Intake`.
|
||||||
|
|
||||||
|
Это отдельная новая вкладка проекта.
|
||||||
|
|
||||||
|
### 2. Обычная задача в целевом проекте
|
||||||
|
|
||||||
|
Задача не должна висеть в ручном triage.
|
||||||
|
|
||||||
|
После отправки она должна сразу попадать в обычный workflow целевого проекта, в его default-state.
|
||||||
|
|
||||||
|
### 3. Статусная пришлепка в источнике
|
||||||
|
|
||||||
|
У каждой записи во `Внешних контурах` в списке должен быть видимый статус.
|
||||||
|
|
||||||
|
Для первой версии правильнее показывать фактическое имя текущего статуса целевой задачи:
|
||||||
|
- `В плане`
|
||||||
|
- `К выполнению`
|
||||||
|
- `В работе`
|
||||||
|
- `Готово`
|
||||||
|
- `Отменено`
|
||||||
|
- либо любой другой кастомный статус целевого проекта
|
||||||
|
|
||||||
|
То есть источник видит не абстрактное `accepted`, а реальный текущий статус исполнения.
|
||||||
|
|
||||||
|
### 4. Зеркалирование изменений из целевого проекта в источник
|
||||||
|
|
||||||
|
Изменения в целевой задаче должны отражаться в исходной карточке внешнего контура.
|
||||||
|
|
||||||
|
Минимальный обязательный набор:
|
||||||
|
- изменение статуса
|
||||||
|
- изменение названия
|
||||||
|
- изменение описания
|
||||||
|
- добавление/удаление файлов
|
||||||
|
- комментарии и служебные заметки по задаче
|
||||||
|
- закрытие/отмена задачи
|
||||||
|
|
||||||
|
### 5. Уведомления инициатору
|
||||||
|
|
||||||
|
Инициатор должен получать уведомления, когда:
|
||||||
|
- задача принята в работу
|
||||||
|
- задача переведена в другой статус
|
||||||
|
- добавлен файл
|
||||||
|
- добавлен комментарий
|
||||||
|
- задача завершена
|
||||||
|
- задача отменена
|
||||||
|
|
||||||
|
### 6. Трассировка между источником и целью
|
||||||
|
|
||||||
|
Связь между источником и целевой задачей должна быть явной и восстановимой.
|
||||||
|
|
||||||
|
Нельзя делать фичу как “создали задачу и забыли”.
|
||||||
|
|
||||||
|
## Архитектурный подход
|
||||||
|
|
||||||
|
## Общая схема
|
||||||
|
|
||||||
|
Используем существующий intake bridge как транспортный слой:
|
||||||
|
- создаем `Issue` в целевом проекте
|
||||||
|
- создаем `IntakeIssue`
|
||||||
|
- сразу переводим bridge в `ACCEPTED`
|
||||||
|
- целевая задача попадает в default-state проекта
|
||||||
|
|
||||||
|
Но только этого уже недостаточно.
|
||||||
|
|
||||||
|
Из-за требований на статусную пришлепку, зеркалирование файлов и уведомления нужен еще source-side слой отображения.
|
||||||
|
|
||||||
|
## Что остается от текущей идеи варианта 2
|
||||||
|
|
||||||
|
Сохраняем:
|
||||||
|
- reuse intake backend-механики
|
||||||
|
- отдельный orchestration endpoint
|
||||||
|
- отдельный фронтовый модуль `Внешние контуры`
|
||||||
|
|
||||||
|
Расширяем:
|
||||||
|
- добавляем источник правды для source-side карточки
|
||||||
|
- добавляем синхронизацию изменений из target issue в source representation
|
||||||
|
- добавляем уведомления
|
||||||
|
|
||||||
|
## Рекомендуемая модель данных для первой итерации
|
||||||
|
|
||||||
|
### Обязательная связь
|
||||||
|
|
||||||
|
Нужна явная пара:
|
||||||
|
- `source_project_id`
|
||||||
|
- `source_request_id`
|
||||||
|
- `target_project_id`
|
||||||
|
- `target_issue_id`
|
||||||
|
|
||||||
|
### Метаданные bridge
|
||||||
|
|
||||||
|
В `IntakeIssue.extra` храним:
|
||||||
|
- тип моста: `external-contours`
|
||||||
|
- источник
|
||||||
|
- цель
|
||||||
|
- автора
|
||||||
|
- временные метки
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bridge": "external-contours",
|
||||||
|
"source_project_id": "uuid",
|
||||||
|
"source_project_name": "Менеджеры",
|
||||||
|
"target_project_id": "uuid",
|
||||||
|
"target_project_name": "Бухгалтерия",
|
||||||
|
"requested_by_id": "uuid",
|
||||||
|
"requested_by_name": "Иван Петров",
|
||||||
|
"requested_at": "2026-04-18T19:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source-side представление
|
||||||
|
|
||||||
|
Для source-side части есть два пути:
|
||||||
|
|
||||||
|
1. Легкая проекция без отдельной таблицы
|
||||||
|
- список `Отправленные` собирается по `IntakeIssue.extra`
|
||||||
|
- детали берутся из целевой задачи
|
||||||
|
|
||||||
|
2. Отдельная source-side сущность или shadow-record
|
||||||
|
- отдельная запись для внешнего контура в проекте-источнике
|
||||||
|
- в ней хранится mirrored state
|
||||||
|
|
||||||
|
Для требований текущего этапа безопаснее считать, что source-side проекция понадобится.
|
||||||
|
|
||||||
|
Причина:
|
||||||
|
- нужен список со статусом
|
||||||
|
- нужны файлы в источнике
|
||||||
|
- нужна история изменений
|
||||||
|
- нужны уведомления
|
||||||
|
|
||||||
|
Простого “читать target issue на лету” для этого, скорее всего, будет мало.
|
||||||
|
|
||||||
|
## UX первой рабочей вертикали
|
||||||
|
|
||||||
|
## Модуль `Внешние контуры`
|
||||||
|
|
||||||
|
В проекте появляется новая вкладка:
|
||||||
|
- `Внешние контуры`
|
||||||
|
|
||||||
|
Внутри:
|
||||||
|
- кнопка `Новый внешний запрос`
|
||||||
|
- список `Открытые`
|
||||||
|
- список `Завершенные`
|
||||||
|
|
||||||
|
## Форма создания
|
||||||
|
|
||||||
|
Поля:
|
||||||
|
- целевой проект
|
||||||
|
- название
|
||||||
|
- описание
|
||||||
|
- исполнитель из целевого проекта
|
||||||
|
- приоритет
|
||||||
|
- срок
|
||||||
|
- метки
|
||||||
|
|
||||||
|
Вложения можно заложить сразу в спецификацию, но в первую техническую поставку лучше включать аккуратно.
|
||||||
|
|
||||||
|
## Карточка в списке
|
||||||
|
|
||||||
|
В строке списка должны быть:
|
||||||
|
- название
|
||||||
|
- целевой проект
|
||||||
|
- исполнитель
|
||||||
|
- дата создания
|
||||||
|
- статусная пришлепка
|
||||||
|
- индикатор новых изменений
|
||||||
|
|
||||||
|
## Детальный экран внешнего контура
|
||||||
|
|
||||||
|
Детальный экран источника не должен быть просто копией target issue.
|
||||||
|
|
||||||
|
Правильнее собрать его из блоков:
|
||||||
|
- исходный запрос
|
||||||
|
- текущий статус
|
||||||
|
- целевой проект и исполнитель
|
||||||
|
- история изменений
|
||||||
|
- синхронизированные файлы
|
||||||
|
- комментарии/обновления
|
||||||
|
|
||||||
|
Это безопаснее, чем пытаться перетирать исходное описание данными целевого проекта.
|
||||||
|
|
||||||
|
## Правила синхронизации
|
||||||
|
|
||||||
|
## Направления
|
||||||
|
|
||||||
|
### Источник -> цель
|
||||||
|
|
||||||
|
На этапе создания отправляем:
|
||||||
|
- название
|
||||||
|
- описание
|
||||||
|
- исполнителя
|
||||||
|
- приоритет
|
||||||
|
- срок
|
||||||
|
- метки
|
||||||
|
|
||||||
|
### Цель -> источник
|
||||||
|
|
||||||
|
После создания отражаем обратно:
|
||||||
|
- текущий статус
|
||||||
|
- изменения описания
|
||||||
|
- комментарии
|
||||||
|
- файлы
|
||||||
|
- итоговый результат
|
||||||
|
|
||||||
|
## Что должно быть синхронизировано в обязательном порядке
|
||||||
|
|
||||||
|
- `target issue state.name`
|
||||||
|
- `target issue updated_at`
|
||||||
|
- файлы target issue
|
||||||
|
- пользовательские комментарии
|
||||||
|
- итоговый resolution state
|
||||||
|
|
||||||
|
## Что пока не нужно считать обязательным
|
||||||
|
|
||||||
|
- полная двусторонняя редакция из источника после отправки
|
||||||
|
- редактирование чужих project labels через источник
|
||||||
|
- полный realtime-collab режим
|
||||||
|
|
||||||
|
## Уведомления
|
||||||
|
|
||||||
|
Минимальный набор событий:
|
||||||
|
- запрос создан
|
||||||
|
- задача принята
|
||||||
|
- статус изменен
|
||||||
|
- добавлен файл
|
||||||
|
- добавлен комментарий
|
||||||
|
- задача завершена
|
||||||
|
- задача отменена
|
||||||
|
|
||||||
|
Каналы первой версии:
|
||||||
|
- in-app notifications
|
||||||
|
|
||||||
|
Каналы последующих итераций:
|
||||||
|
- email
|
||||||
|
- внешние integrations
|
||||||
|
|
||||||
|
## Ограничения текущего этапа
|
||||||
|
|
||||||
|
### 1. Вложения сейчас хрупкие
|
||||||
|
|
||||||
|
Текущий runtime уже показывал хрупкость attachment flow.
|
||||||
|
|
||||||
|
Поэтому вложения нужно учитывать в дизайне, но внедрять аккуратно и тестировать отдельно.
|
||||||
|
|
||||||
|
### 2. Права сложнее, чем кажется
|
||||||
|
|
||||||
|
Отправитель должен иметь право отправлять запрос из source project, но не обязан быть участником target project.
|
||||||
|
|
||||||
|
При этом:
|
||||||
|
- target assignee должен быть участником target project
|
||||||
|
- target issue создается в target project
|
||||||
|
- источник должен иметь возможность видеть результат обработки без полного membership в target project
|
||||||
|
|
||||||
|
### 3. Простая “копия intake” уже недостаточна
|
||||||
|
|
||||||
|
Как только добавляются:
|
||||||
|
- статусная пришлепка
|
||||||
|
- файлы
|
||||||
|
- история
|
||||||
|
- уведомления
|
||||||
|
|
||||||
|
фича перестает быть просто формой создания.
|
||||||
|
|
||||||
|
Нужен отдельный источник правды для source-side представления.
|
||||||
|
|
||||||
|
## Что считаем текущим этапом
|
||||||
|
|
||||||
|
Текущий этап — это не финальная реализация, а проектирование и запуск вертикального среза.
|
||||||
|
|
||||||
|
В этот этап входят:
|
||||||
|
- фиксация терминологии
|
||||||
|
- фиксация архитектурного подхода
|
||||||
|
- разбиение на поставки
|
||||||
|
- определение обязательных требований для первой версии
|
||||||
|
- определение границ MVP
|
||||||
|
|
||||||
|
Подробная поэтапная разработка описана в:
|
||||||
|
- [phase-roadmap.md](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/docs_prod/cross-project-task-routing/phase-roadmap.md)
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
# Этапы разработки
|
||||||
|
|
||||||
|
## Общий принцип
|
||||||
|
|
||||||
|
Не делаем сразу тяжелую доменную платформу.
|
||||||
|
|
||||||
|
Идем вертикальными поставками:
|
||||||
|
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 список уже видит факт отправки
|
||||||
|
|
||||||
|
## Этап 2. Source-side список и статусная пришлепка
|
||||||
|
|
||||||
|
### Цель
|
||||||
|
|
||||||
|
Дать инициатору читаемый контроль за отправленными запросами.
|
||||||
|
|
||||||
|
### Что входит
|
||||||
|
|
||||||
|
- список `Открытые`
|
||||||
|
- список `Завершенные`
|
||||||
|
- текущий статус задачи как пришлепка
|
||||||
|
- отображение целевого проекта
|
||||||
|
- отображение исполнителя
|
||||||
|
- отображение даты обновления
|
||||||
|
- индикатор новых изменений
|
||||||
|
|
||||||
|
### Критерий приемки
|
||||||
|
|
||||||
|
- в проекте-источнике виден список отправленных запросов
|
||||||
|
- у каждой записи отображается актуальный статус целевой задачи
|
||||||
|
|
||||||
|
## Этап 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 карточке
|
||||||
|
|
||||||
|
## Этап 4. Уведомления
|
||||||
|
|
||||||
|
### Цель
|
||||||
|
|
||||||
|
Не заставлять инициатора вручную проверять изменения.
|
||||||
|
|
||||||
|
### Что входит
|
||||||
|
|
||||||
|
- in-app notification на:
|
||||||
|
- принятие
|
||||||
|
- перевод статуса
|
||||||
|
- добавление файла
|
||||||
|
- новый комментарий
|
||||||
|
- завершение
|
||||||
|
- отмену
|
||||||
|
|
||||||
|
### Критерий приемки
|
||||||
|
|
||||||
|
- инициатор получает уведомления по ключевым событиям жизненного цикла внешнего запроса
|
||||||
|
|
||||||
|
## Этап 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 карточка должна быть единственной точкой просмотра
|
||||||
|
|
||||||
|
## Рекомендуемый порядок фактической разработки
|
||||||
|
|
||||||
|
1. Этап 0
|
||||||
|
2. Этап 1
|
||||||
|
3. Этап 2
|
||||||
|
4. Этап 3
|
||||||
|
5. Этап 4
|
||||||
|
6. Этап 5
|
||||||
|
|
||||||
|
Это даст быстрый полезный результат и не загонит проект в ранний тяжелый refactor.
|
||||||
Loading…
Reference in New Issue