# Этапы разработки ## Общий принцип Не делаем сразу тяжелую доменную платформу. Идем вертикальными поставками: 1. сначала транспорт и создание задачи 2. потом source-side представление 3. потом синхронизация 4. потом уведомления и полировка ## Freeze point на 2026-04-19 Текущий вертикальный срез временно заморожен. На момент freeze point уже есть: - отправка запроса из source project в target project того же `workspace` - отсутствие требования прямого membership в target project для отправки - source-side список `Открытые / Завершенные` - source-side карточка на shell `Предложений` - source-side редактирование открытого запроса по `заголовку` и `описанию` - зеркалирование статуса, комментариев, вложений и activity из целевого контура - source-side действия `Принять`, `Отклонить`, `Ответ во внешний контур` - индикатор непрочитанных изменений - карточка `Маршрутизация` в целевом формате `3 x 3` Дальше по roadmap пока не идем, пока не приняты продуктовые решения по внутреннему жизненному циклу принятого запроса. ## Этап 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 решение `accepted` - `Принять` создает отдельную сущность во `Внутреннем контуре` - `Принять` переводит карточку в отдельный внутренний статус без создания дубликата Это главный блокирующий вопрос перед следующим этапом. ### 2. Где дальше живет принятый запрос Нужно решить: - карточка остается во `Внешних контурах` как историческая запись - карточка уходит во `Внутренний контур` - карточка одновременно видна в обоих местах, но с разной ролью ### 3. Нужна ли отдельная сущность source-side Нужно принять решение: - достаточно ли текущей source-side проекции поверх bridge metadata - или уже пора вводить отдельную таблицу/модель для внешних контуров ### 4. Файлы Нужно решить: - достаточно ли proxy-доступа к файлам целевой задачи - или файлы надо физически копировать в source-side representation - нужно ли зеркалировать inline-файлы из описания и комментариев ### 5. Комментарии Нужно решить: - source-side reply остается облегченной обратной связью - или нужен полноценный двусторонний поток комментариев как единый discussion-thread ### 6. Уровень realtime Нужно решить: - хватает ли polling для PoC - или следующий этап уже должен включать push/realtime события ### 7. Счетчики и вкладки Нужно решить: - нужен ли отдельный unread-counter по вкладкам `Открытые / Завершенные` - нужен ли отдельный сегмент для запросов, принятых во `Внутренний контур` ### 8. Право редактирования после отправки Нужно решить: - отправитель редактирует только `заголовок` и `описание` - или после отправки он может менять еще `срок`, `назначенного`, `приоритет`, `метки` ### 9. Доступ к target issue Нужно решить: - должен ли инициатор иметь прямую ссылку на target issue - или source-side карточка должна оставаться единственной точкой просмотра для пользователей без membership Текущее решение: - при отсутствии membership в target project прямой переход в target issue скрывается - карточка остается доступной из source project ## Рекомендуемый порядок фактической разработки 1. Этап 0 2. Этап 1 3. Этап 2 4. Этап 3 5. Этап 4 6. Этап 5 Это даст быстрый полезный результат и не загонит проект в ранний тяжелый refactor.