3517 lines
158 KiB
Markdown
3517 lines
158 KiB
Markdown
# Task Queue / Agenda Rollout Plan
|
||
|
||
**Date:** 2026-04-21
|
||
**Status:** Proposed
|
||
**Scope:** Team Management, MCP task surfaces, agent operational queue
|
||
|
||
---
|
||
|
||
## TL;DR
|
||
|
||
✅ Базовое решение:
|
||
|
||
- перед полноценным agenda rollout сначала делаем **Phase 0 hardening** для слабых сигналов
|
||
- source of truth остаётся в сырых task/review/kanban файлах
|
||
- в Phase 1 не заводим второй persisted projection-файл
|
||
- вместо этого строим **derived queue projection on read** через **controller-owned board snapshot** под **team-level lock**
|
||
- вводим явные derived-поля: `actionOwner`, `nextAction`, `queueCategory`, `watchers`
|
||
- разделяем 4 разных surface:
|
||
- `member_briefing` = только bootstrap + правила + роль
|
||
- `task_briefing` = каноническая очередь конкретного участника
|
||
- `lead_briefing` = каноническая очередь лида
|
||
- `task_list` = inventory/search surface, который постепенно уходит в filtered role
|
||
- Phase 2 добавляет `revision` и только потом, если реально нужно по профайлингу, `delta`
|
||
|
||
Главная мысль: проблема не в том, что `task_list` "слишком длинный". Проблема в том, что он сейчас смешивает **inventory**, **workflow state**, **операционный приоритет** и **шум**, заставляя LLM самому угадывать "что мне делать сейчас".
|
||
|
||
⚠️ После дополнительного code review важны 4 уточнения:
|
||
|
||
- сейчас в коде нет общего board-level lock поверх task + kanban + review mutations, но Phase 0 надо ограничить controller snapshot/mutation scope, а не пытаться сразу перевести под него все main-side readers
|
||
- reviewer для review-задачи в Phase 0/1 надо выводить только из текущего review cycle в `historyEvents`, потому что оба write-path сегодня заводят kanban review entry с `reviewer: null`
|
||
- `needsClarification: "lead"` в коде сейчас auto-clear'ится шире, чем обещают prompt-инструкции, поэтому safest Phase 0 - explicit clear only
|
||
- `task_list` нельзя резко ломать сменой default semantics, потому что lead prompts уже используют его как full inventory entrypoint
|
||
|
||
---
|
||
|
||
## 1. Почему вообще понадобился этот redesign
|
||
|
||
Сейчас у системы есть 2 разных read-path, но ни один из них не задаёт правильную operational semantics:
|
||
|
||
### `task_list`
|
||
|
||
- технически возвращает почти весь список задач команды
|
||
- это не "очередь работы", а полу-сырой inventory dump
|
||
- он слишком тяжёлый по payload
|
||
- главное, он **не говорит явно**, кто сейчас должен действовать по задаче
|
||
- агенту приходится самому вычислять:
|
||
- моя ли это задача
|
||
- жду ли я ревью
|
||
- я ли ревьюер
|
||
- надо ли пинговать лида
|
||
- задача реально actionable или просто informational
|
||
|
||
### `task_briefing`
|
||
|
||
- даёт более компактное представление
|
||
- но сейчас завязан в основном на `owner === memberName`
|
||
- поэтому не покрывает важные сценарии:
|
||
- задача формально owned одним участником, но action сейчас у ревьюера
|
||
- задача зависла из-за отсутствующего reviewer
|
||
- задача ждёт решения лида
|
||
- участнику нужна awareness по своим задачам, даже если actionOwner временно не он
|
||
|
||
Итог:
|
||
|
||
- лид не получает нормальную operational queue
|
||
- тиммейт может не видеть "что от него реально требуется"
|
||
- LLM тратит токены и качество на вывод политики из сырых полей
|
||
- при росте команды и количества задач путаница становится системной
|
||
|
||
---
|
||
|
||
## 2. Что именно не так в текущей модели
|
||
|
||
### 2.1 `task_list` сегодня слишком близок к raw dump
|
||
|
||
По факту текущий `task_list` делает почти "отдать все задачи, слегка урезав крупные поля". Это blocklist-подход:
|
||
|
||
- убираются тяжёлые поля вроде комментариев и истории
|
||
- почти всё остальное сохраняется
|
||
|
||
Такой подход плох не только по размеру, но и по смыслу:
|
||
|
||
- агенту всё ещё видны почти все задачи команды
|
||
- инструмента нет opinionated workflow surface
|
||
- список не говорит, какие карточки именно должны попасть в текущую очередь участника
|
||
|
||
### 2.2 `task_briefing` сегодня не отделяет action от awareness
|
||
|
||
Сейчас логика ближе к "покажи мои assigned tasks". Но operational queue и assigned list не одно и то же.
|
||
|
||
Примеры:
|
||
|
||
- owner = `alice`, reviewer = `bob`, задача в review
|
||
`alice` должна понимать, что задача не пропала, но actionOwner уже `bob`
|
||
- задача completed, reviewer ещё не назначен
|
||
action нужен не owner, а лиду
|
||
- задача ждёт ответа от user
|
||
actionOwner = `user`, но лид должен видеть это как oversight item
|
||
|
||
### 2.3 Raw task list не должен быть основным surface для LLM
|
||
|
||
LLM лучше работает, когда система даёт:
|
||
|
||
- кто сейчас должен действовать
|
||
- почему именно он
|
||
- какое следующее ожидаемое действие
|
||
- что informational, а что actionable
|
||
|
||
LLM хуже работает, когда ему дают 50 карточек и ожидают, что он сам выведет workflow policy.
|
||
|
||
### 2.4 Важные факты из текущего кода
|
||
|
||
Ниже то, на что уже можно опираться в rollout, без изобретения новой подсистемы с нуля:
|
||
|
||
- `task_list` сегодня реально отдаёт почти весь team inventory в урезанном виде, а не opinionated queue
|
||
- `task_briefing` сегодня в основном строится через `owner === memberName`
|
||
- `review_request` технически может отправить задачу в review даже без явно резолвленного reviewer
|
||
- active reviewer для agenda-safe routing в Phase 0/1 надо выводить только из текущего review cycle в `historyEvents`; per-task kanban reviewer сейчас не является надёжным signal
|
||
- runtime identity для участника уже умеет частично резолвиться из существующего runtime context
|
||
- UI и MCP уже в основном идут через `agent-teams-controller`, то есть rollout можно делать эволюционно вокруг текущего controller layer
|
||
- task и kanban сегодня пишутся atomically по файлам, но не transactionally как единый board update
|
||
- controller-side task/kanban write paths сейчас вообще не используют существующий `withFileLockSync(...)`; текущий lock primitive живёт отдельно, синхронный, busy-wait, с acquire timeout `5s` и stale timeout `30s`
|
||
- `review_request` сейчас делает multi-step sequence `kanban -> task history -> message`, значит queue read действительно может увидеть промежуточное состояние
|
||
- `review_request` при ошибке отправки уведомления сейчас делает только `kanban.clear`, но уже записанный `review_requested` history event не откатывает
|
||
- `needsClarification: "lead"` в task store сейчас auto-clear'ится при комментарии любого не-owner автора, а не только лида
|
||
- `task_list` использует blocklist-подход, поэтому payload может незаметно разрастаться при появлении новых task-полей
|
||
- текущий `task_list` в MCP напрямую вызывает `controller.tasks.listTasks()` без фильтров и затем прогоняет результат через `slimTaskForList(...)`, то есть filter-capable inventory contract пока даже не выбран
|
||
- `task_list` входит в teammate operational tool catalog, значит prompt-only migration недостаточна - нужен ещё catalog/access rollout
|
||
- в репо есть как минимум две локальные `.d.ts` декларации для `agent-teams-controller`, и они уже расходятся по полноте surface
|
||
- `mcp-server` тесты сейчас явно проверяют текущую blocklist-semantics `task_list`, значит migration должна обновлять и test contract, а не только runtime
|
||
- `owner` и `needsClarification` хранятся как текущие поля task payload, но не имеют такого же history-backed контракта, как review transitions
|
||
- task logs умеют видеть `task_set_owner` / `task_set_clarification`, но это отдельный observability слой, а не базовый queue source of truth
|
||
- в schema у `kanban-state.tasks[taskId]` есть `reviewer`, но официальный mutation patch сейчас не несёт reviewer как поддерживаемый per-task signal
|
||
- prompt знает правило вида "если есть reviewer/qa/tech-lead, отправляй на review", но это не равняется канонической machine-readable review policy в runtime state
|
||
- roster resolution уже существует минимум в двух вариантах: controller-side `resolveTeamMembers(...)` и main/UI-side `TeamMemberResolver`, и они нормализуют состав команды не полностью одинаково
|
||
- main-side `TeamDataService` compatibility layer сейчас резолвит `reviewer` как `kanbanTaskState.reviewer ?? history(review_approved|review_started|review_requested)`, а `reviewState` берёт из persisted field, что уже шире и слабее, чем queue-safe current-cycle contract
|
||
- `setTaskOwner` и `task_set_clarification` меняют task payload и `updatedAt`, но не добавляют workflow events в `historyEvents`
|
||
- stall-monitor уже использует более строгую review-window semantics, где открытое review окно начинается с `review_started`, а не просто с `review_requested`
|
||
|
||
Это важно, потому что план не требует:
|
||
|
||
- переписывать storage
|
||
- менять формат task-файлов
|
||
- вводить новый отдельный board runtime
|
||
|
||
---
|
||
|
||
## 3. Цели redesign
|
||
|
||
Нужно добиться одновременно 5 свойств:
|
||
|
||
1. **Однозначность**
|
||
По каждой активной задаче должен существовать один primary `actionOwner` или явно `none/user`.
|
||
|
||
2. **Компактность**
|
||
Обычный teammate не должен получать весь board dump только ради того, чтобы понять свои 2-4 задачи.
|
||
|
||
3. **Надёжность**
|
||
Read model не должен становиться вторым нестабильным source of truth.
|
||
|
||
4. **Понятная иерархия surfaces**
|
||
Bootstrap, operational queue, full context и inventory/search должны быть разными инструментами.
|
||
|
||
5. **Эволюционность**
|
||
Phase 1 должен сильно улучшить поведение без большого риска stale projection bugs.
|
||
|
||
---
|
||
|
||
## 3.1 Confidence Map After Code Review
|
||
|
||
Ниже зоны, где после просмотра кода у нас разная степень уверенности.
|
||
|
||
### A. Derived agenda как базовый read model
|
||
|
||
`🎯 10 🛡️ 10 🧠 6`
|
||
|
||
Почему уверенность высокая:
|
||
|
||
- это не требует второго durable state
|
||
- current storage уже содержит достаточно сигналов для derived routing
|
||
- основной риск здесь не идея, а discipline around rollout
|
||
|
||
### B. Controller-owned board snapshot + lock
|
||
|
||
`🎯 9 🛡️ 9 🧠 5`
|
||
|
||
Почему уверенность выросла:
|
||
|
||
- после дополнительного просмотра видно, что main-side raw readers (`TeamTaskReader`, `TeamKanbanManager`) живут отдельно и не требуют немедленного включения в agenda path
|
||
- для agent-facing queue достаточно сначала сделать один канонический controller snapshot contract
|
||
- это сильно уже и безопаснее, чем пытаться в Phase 0 синхронно унифицировать весь repo вокруг одного lock API
|
||
|
||
### C. Reviewer resolution
|
||
|
||
`🎯 8 🛡️ 9 🧠 4`
|
||
|
||
Почему уверенность стала выше:
|
||
|
||
- и controller-side `kanbanStore.setKanbanColumn(...)`, и main-side `TeamKanbanManager.updateTask(...)` при переводе в `review` записывают `reviewer: null`
|
||
- значит ambiguity здесь на самом деле меньше, а не больше: Phase 0/1 просто не должны опираться на per-task kanban reviewer
|
||
- остаётся чётко определить только current-cycle precedence внутри `historyEvents`
|
||
|
||
### D. Clarification semantics
|
||
|
||
`🎯 8 🛡️ 10 🧠 3`
|
||
|
||
Это всё ещё слабая зона текущего runtime, но у неё теперь есть понятный safe fallback.
|
||
|
||
Причина:
|
||
|
||
- prompts говорят одно
|
||
- код auto-clear'ит флаг шире
|
||
- но safest fix очень прямой: в Phase 0 убрать implicit auto-clear из routing contract и жить через explicit `task_set_clarification clear`
|
||
|
||
### E. `task_list` migration без silent breakage
|
||
|
||
`🎯 7 🛡️ 7 🧠 5`
|
||
|
||
Почему:
|
||
|
||
- целевая роль `task_list` понятна
|
||
- но менять его default резко опасно, потому что старые лид-промпты уже ожидают текущую семантику
|
||
- значит migration должна идти через новый canonical surface, а не через скрытую подмену смысла
|
||
|
||
### F. Queue-side roster normalization
|
||
|
||
`🎯 7 🛡️ 8 🧠 5`
|
||
|
||
Почему это остаётся важной зоной:
|
||
|
||
- controller-side `resolveTeamMembers(...)` и UI-side `TeamMemberResolver` до сих пор фильтруют roster не полностью одинаково
|
||
- для queue это критично, потому что invalid owner/reviewer routing зависит от того, кого мы вообще считаем валидным member
|
||
- но здесь достаточно зафиксировать минимальный queue contract и покрыть его тестами, без большого package refactor
|
||
|
||
### G. Lead identity normalization for `lead_briefing`
|
||
|
||
`🎯 6 🛡️ 8 🧠 4`
|
||
|
||
Почему это остаётся слабее других зон:
|
||
|
||
- controller-side `inferLeadName(...)` использует ad hoc heuristics: `agentType === "team-lead"`, `role` содержит `lead`, имя `team-lead`, иначе первый config member
|
||
- shared `leadDetection` utilities уже живут по чуть более другому контракту
|
||
- для `lead_briefing` и future ergonomic clarification clear нельзя опираться на "может быть это и есть lead" без явного phase rule
|
||
|
||
### H. Tool-catalog scoping for `lead_briefing`
|
||
|
||
`🎯 8 🛡️ 9 🧠 4`
|
||
|
||
Почему это важно:
|
||
|
||
- текущий `mcpToolCatalog` режет доступность по group-level `teammateOperational`, а не по per-tool policy
|
||
- если просто положить `lead_briefing` в existing task-group, он автоматически окажется в teammate operational tool set
|
||
- это не runtime bug, но это прямой путь к лишнему шуму и путанице у teammate agents
|
||
|
||
---
|
||
|
||
## 3.2 Top 3 Options In The Least Certain Areas
|
||
|
||
Ниже не "все возможные идеи", а именно те развилки, где реально можно ошибиться архитектурно.
|
||
|
||
### A. Где должен жить shared board lock
|
||
|
||
**1. Controller-owned board snapshot API + controller-local team lock**
|
||
|
||
`🎯 10 🛡️ 9 🧠 5`
|
||
|
||
Примерный объём: `80-160` строк изменений
|
||
|
||
Суть:
|
||
|
||
- внутри `agent-teams-controller` ввести один канонический `withTeamBoardLock(...)` или `getBoardSnapshot(...)`
|
||
- под него завести multi-file mutations review/task/kanban и agenda reads
|
||
- main-process не заставлять в той же фазе массово переходить на этот primitive, если ему не нужен именно agenda-grade snapshot
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- один semantic owner для agent-facing queue
|
||
- не раздувает Phase 0 до cross-layer refactor всей файловой модели
|
||
- оставляет возможность позже экспортировать тот же snapshot наружу, если он реально понадобится UI
|
||
|
||
**2. Exported generic shared primitive across controller + main**
|
||
|
||
`🎯 7 🛡️ 8 🧠 6`
|
||
|
||
Примерный объём: `100-180` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сделать универсальный lock/snapshot contract, который одинаково используют controller и main
|
||
|
||
Риск:
|
||
|
||
- полезно в долгую, но для ближайшего rollout это уже больше работа, чем нужно
|
||
- есть риск начать чинить "архитектурную красоту" вместо реально агентского queue path
|
||
|
||
**3. Пер-file lock orchestration без отдельного team lock**
|
||
|
||
`🎯 3 🛡️ 3 🧠 6`
|
||
|
||
Примерный объём: `60-120` строк изменений
|
||
|
||
Суть:
|
||
|
||
- пытаться добиться консистентности через набор локов на task/kanban файлы
|
||
|
||
Риск:
|
||
|
||
- сложнее reasoning
|
||
- выше риск partial interleaving
|
||
- хуже читается и хуже тестируется
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- вариант 2 рассматривать только если после rollout тот же snapshot contract реально понадобится non-controller consumers
|
||
|
||
### B. Как резолвить reviewer для active review cycle
|
||
|
||
**1. Pure history-first current-cycle resolver**
|
||
|
||
`🎯 10 🛡️ 10 🧠 4`
|
||
|
||
Примерный объём: `70-140` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сканировать history backwards только в пределах текущего review cycle
|
||
- использовать `review_started.actor` и `review_requested.reviewer` как основные сигналы
|
||
- в Phase 0/1 вообще **не использовать** `kanban-state.tasks[taskId].reviewer` как routing signal
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- максимально опирается на уже существующие durable events
|
||
- согласуется с уже существующей derivation логикой review state через history
|
||
- это полностью соответствует реальному коду: и controller, и main сейчас создают review-entry с `reviewer: null`
|
||
|
||
**2. History-first now, kanban fallback only after writer hardening**
|
||
|
||
`🎯 6 🛡️ 8 🧠 6`
|
||
|
||
Примерный объём: `110-210` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сначала расширить write-path так, чтобы per-task `reviewer` реально persist'ился
|
||
- только после этого разрешить kanban fallback в resolver
|
||
|
||
Риск:
|
||
|
||
- это уже не Phase 0 hardening, а изменение durable semantics
|
||
- если делать одновременно с agenda rollout, возрастает blast radius
|
||
|
||
**3. Новый persisted reviewer slot в task schema**
|
||
|
||
`🎯 7 🛡️ 8 🧠 7`
|
||
|
||
Примерный объём: `120-240` строк изменений
|
||
|
||
Суть:
|
||
|
||
- добавить explicit reviewer прямо в task payload и синхронизировать его в review flow
|
||
|
||
Плюсы:
|
||
|
||
- проще future reads
|
||
|
||
Минусы:
|
||
|
||
- новая durable schema responsibility
|
||
- выше цена ошибок и миграции
|
||
|
||
Выбор:
|
||
|
||
- для Phase 0/1 брать **вариант 1**
|
||
- вариант 3 оставлять как later optimization, только если history-based resolver окажется недостаточным
|
||
|
||
### C. Как harden'ить clarification clearing
|
||
|
||
**1. Phase 0 explicit-clear-only semantics**
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Примерный объём: `25-60` строк изменений
|
||
|
||
Суть:
|
||
|
||
- не делаем никакой implicit auto-clear частью routing contract
|
||
- clarification снимается только через явный `task_set_clarification clear`
|
||
- prompts и briefing обновляются синхронно
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- максимально надёжно
|
||
- убирает silent false negatives в очереди
|
||
- делает поведение легко тестируемым и объяснимым
|
||
|
||
**2. Controller-layer lead-aware auto-clear after alias hardening**
|
||
|
||
`🎯 8 🛡️ 9 🧠 6`
|
||
|
||
Примерный объём: `70-140` строк изменений
|
||
|
||
Суть:
|
||
|
||
- после стабилизации lead identity вернуть удобство:
|
||
- `lead` clarification clear'ится на комментарий лида
|
||
- `user` clarification clear'ится на комментарий пользователя
|
||
|
||
Риск:
|
||
|
||
- нужно сначала жёстко определить:
|
||
- canonical lead identity
|
||
- alias policy (`team-lead`, фактическое имя лида, возможный `lead`)
|
||
- valid actor normalization
|
||
|
||
**3. Протянуть explicit `canClearClarification` / `leadName` в taskStore API**
|
||
|
||
`🎯 5 🛡️ 6 🧠 6`
|
||
|
||
Примерный объём: `50-110` строк изменений
|
||
|
||
Суть:
|
||
|
||
- taskStore всё ещё делает auto-clear, но получает workflow policy сверху
|
||
|
||
Риск:
|
||
|
||
- API store начинает знать слишком много про workflow policy
|
||
|
||
Выбор:
|
||
|
||
- для Phase 0 брать **вариант 1**
|
||
- вариант 2 оставлять как optional ergonomics pass только после стабилизации lead alias semantics
|
||
|
||
Отдельное уточнение:
|
||
|
||
- это не двухстрочная правка в storage layer
|
||
- `taskStore.addTaskComment()` сам по себе не знает, кто является lead для команды
|
||
|
||
Поэтому "clear only when lead answers" нельзя надёжно реализовать внутри store без:
|
||
|
||
- переноса policy выше, в controller/context layer
|
||
- или явной передачи lead-aware policy signal в storage API
|
||
|
||
### D. Как делать queue revision в Phase 2
|
||
|
||
**1. Stable hash of compact agenda DTO**
|
||
|
||
`🎯 9 🛡️ 9 🧠 4`
|
||
|
||
Примерный объём: `40-90` строк изменений
|
||
|
||
Суть:
|
||
|
||
- как в `feedRevision`, считать hash от уже нормализованного compact agenda payload
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- не требует extra durable file
|
||
- легко проверять
|
||
- удобно для `unchanged` short-circuit
|
||
|
||
**2. Team directory mtime / file timestamps**
|
||
|
||
`🎯 4 🛡️ 4 🧠 3`
|
||
|
||
Примерный объём: `20-40` строк изменений
|
||
|
||
Риск:
|
||
|
||
- платформенная хрупкость
|
||
- плохая детерминированность
|
||
|
||
**3. Dedicated revision counter file**
|
||
|
||
`🎯 7 🛡️ 8 🧠 6`
|
||
|
||
Примерный объём: `70-140` строк изменений
|
||
|
||
Плюсы:
|
||
|
||
- дешёвые reads
|
||
|
||
Минусы:
|
||
|
||
- ещё одна durable write responsibility
|
||
|
||
Выбор:
|
||
|
||
- сначала **вариант 1**
|
||
- вариант 3 рассматривать только если hash on read реально станет bottleneck
|
||
|
||
### E. Где должна жить canonical agenda derivation logic
|
||
|
||
**1. Controller-owned single implementation**
|
||
|
||
`🎯 9 🛡️ 9 🧠 5`
|
||
|
||
Примерный объём: `90-170` строк изменений
|
||
|
||
Суть:
|
||
|
||
- agenda derivation живёт в `agent-teams-controller`
|
||
- MCP tools только оборачивают её
|
||
- main-process, если ему нужен тот же semantic surface, либо вызывает controller API, либо использует тот же exported helper
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- один semantic owner
|
||
- меньше риска drift между MCP/runtime/UI
|
||
- уже сейчас controller является главным write-path для board operations
|
||
|
||
**2. Отдельные реализации в controller и main-process**
|
||
|
||
`🎯 4 🛡️ 4 🧠 4`
|
||
|
||
Примерный объём: `120-220` строк изменений
|
||
|
||
Риск:
|
||
|
||
- review/clarification semantics почти гарантированно разъедутся со временем
|
||
- сложнее тестировать и объяснять различия
|
||
|
||
**3. Новый общий workspace package / shared helper**
|
||
|
||
`🎯 7 🛡️ 8 🧠 7`
|
||
|
||
Примерный объём: `140-260` строк изменений
|
||
|
||
Плюсы:
|
||
|
||
- архитектурно чище в долгую
|
||
|
||
Минусы:
|
||
|
||
- это уже mini-refactor package boundaries
|
||
- для Phase 0/1 цена выше, чем польза
|
||
|
||
Выбор:
|
||
|
||
- для ближайшего rollout брать **вариант 1**
|
||
- вариант 3 оставлять как later cleanup, если agenda surface реально понадобится нескольким runtime слоям в одном виде
|
||
|
||
### F. Как уводить `task_list` от teammate default workflow
|
||
|
||
**1. Prompt + tool-catalog phased migration**
|
||
|
||
`🎯 9 🛡️ 9 🧠 5`
|
||
|
||
Примерный объём: `60-120` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сначала вводим `lead_briefing`
|
||
- затем меняем prompts
|
||
- затем убираем `task_list` из teammate operational catalog или переводим его в less-prominent surface
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- migration управляемая
|
||
- меньше surprising behavior для уже работающих flows
|
||
|
||
**2. Prompt-only migration**
|
||
|
||
`🎯 5 🛡️ 5 🧠 2`
|
||
|
||
Примерный объём: `15-35` строк изменений
|
||
|
||
Риск:
|
||
|
||
- `task_list` всё равно остаётся у тиммейта "под рукой"
|
||
- модель будет продолжать иногда использовать его как shortcut
|
||
|
||
**3. Instant hard removal from teammate catalog**
|
||
|
||
`🎯 6 🛡️ 7 🧠 4`
|
||
|
||
Примерный объём: `20-60` строк изменений
|
||
|
||
Плюсы:
|
||
|
||
- быстро убирает temptation
|
||
|
||
Минусы:
|
||
|
||
- выше шанс сломать существующие prompt assumptions и recovery workflows
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
|
||
### G. Как трактовать "review required" без явной policy-модели
|
||
|
||
**1. Explicit-review-only semantics in Phase 0/1**
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Примерный объём: `20-50` строк изменений
|
||
|
||
Суть:
|
||
|
||
- queue считает review обязательным только когда есть явный review state / review cycle signal
|
||
- completed task без active review не получает auto-routing в `assign_reviewer` только потому, что "в команде есть reviewer"
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- не выдумывает policy, которой в runtime state сейчас нет
|
||
- минимизирует ложные lead action items
|
||
|
||
**2. Inference from free-form member roles**
|
||
|
||
`🎯 3 🛡️ 3 🧠 3`
|
||
|
||
Примерный объём: `20-40` строк изменений
|
||
|
||
Суть:
|
||
|
||
- пытаться читать `role` вроде `reviewer`, `qa`, `tech-lead` и решать, что completed task должна уйти в review
|
||
|
||
Риск:
|
||
|
||
- free-form role text не является надёжным policy contract
|
||
- легко получить false positives и странные queue jumps
|
||
|
||
**3. Introduce explicit review policy field later**
|
||
|
||
`🎯 8 🛡️ 9 🧠 7`
|
||
|
||
Примерный объём: `90-180` строк изменений
|
||
|
||
Суть:
|
||
|
||
- в будущем добавить machine-readable team/task review policy
|
||
- только тогда можно safely строить routing вроде `completed -> assign_reviewer` без explicit review event
|
||
|
||
Выбор:
|
||
|
||
- для Phase 0/1 брать **вариант 1**
|
||
- вариант 3 держать как possible future enhancement
|
||
|
||
### H. Какая roster resolution считается канонической для queue
|
||
|
||
**1. Controller-owned roster normalization for queue**
|
||
|
||
`🎯 8 🛡️ 9 🧠 5`
|
||
|
||
Примерный объём: `50-110` строк изменений
|
||
|
||
Суть:
|
||
|
||
- queue в controller layer использует controller-side roster resolver
|
||
- но этот resolver сначала доводится до **минимально достаточного** predictable contract:
|
||
- removed members
|
||
- lead alias normalization
|
||
- suppression of phantom inbox-derived aliases where necessary
|
||
- ignore qualified external recipients
|
||
- ignore generated/internal pseudo-agent names, если они не пришли из explicit config/meta
|
||
|
||
Плюсы:
|
||
|
||
- queue остаётся рядом со своим runtime context
|
||
- не появляется cross-layer dependency на UI resolver
|
||
|
||
**2. Reuse UI `TeamMemberResolver` semantics indirectly**
|
||
|
||
`🎯 5 🛡️ 6 🧠 7`
|
||
|
||
Примерный объём: `90-170` строк изменений
|
||
|
||
Суть:
|
||
|
||
- пытаться тащить roster rules из main/UI слоя обратно в controller path
|
||
|
||
Риск:
|
||
|
||
- package boundary усложняется
|
||
- для ближайшего rollout это слишком тяжело
|
||
|
||
**3. Shared future roster package/helper**
|
||
|
||
`🎯 7 🛡️ 9 🧠 8`
|
||
|
||
Примерный объём: `120-220` строк изменений
|
||
|
||
Суть:
|
||
|
||
- вынести roster normalization в реально общий helper/package
|
||
|
||
Плюсы:
|
||
|
||
- меньше semantic drift в долгую
|
||
|
||
Минусы:
|
||
|
||
- для Phase 0/1 это уже дополнительный refactor
|
||
|
||
Выбор:
|
||
|
||
- в ближайшем rollout брать **вариант 1**
|
||
- но явно зафиксировать expected queue roster semantics тестами, чтобы drift от UI не был скрытым
|
||
|
||
### I. Где должен жить `lead_briefing` в MCP tool catalog
|
||
|
||
**1. Отдельная `lead` group с `teammateOperational: false`**
|
||
|
||
`🎯 10 🛡️ 10 🧠 4`
|
||
|
||
Примерный объём: `30-70` строк изменений
|
||
|
||
Суть:
|
||
|
||
- добавить отдельную tool group, например `lead`
|
||
- положить туда `lead_briefing`
|
||
- не смешивать этот tool с existing `task` group
|
||
- синхронно расширить `AgentTeamsMcpToolGroupId` в обеих локальных `.d.ts`
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- текущий catalog already group-scoped, а не per-tool scoped
|
||
- это самый дешёвый способ гарантировать, что lead surface не попадёт в `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- semantics остаётся прозрачной: teammate task tools отдельно, lead-only tool отдельно
|
||
|
||
**2. Оставить `lead_briefing` в `task` group, но добавить per-tool denylist override**
|
||
|
||
`🎯 5 🛡️ 7 🧠 6`
|
||
|
||
Примерный объём: `40-90` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сохраняем существующую `task` group
|
||
- поверх неё добавляем дополнительную per-tool политику, чтобы скрыть только `lead_briefing`
|
||
|
||
Риск:
|
||
|
||
- чинит symptom, а не модель
|
||
- делает catalog rules менее очевидными
|
||
- повышает шанс future drift между group semantics и effective availability
|
||
|
||
**3. Оставить `lead_briefing` в `task` group и полагаться только на prompts**
|
||
|
||
`🎯 2 🛡️ 2 🧠 1`
|
||
|
||
Примерный объём: `5-20` строк изменений
|
||
|
||
Суть:
|
||
|
||
- tool физически доступен тиммейтам
|
||
- просто стараемся не упоминать его в teammate prompts
|
||
|
||
Риск:
|
||
|
||
- это не access policy, а надежда на prompt discipline
|
||
- модель всё равно сможет брать лишний tool как shortcut
|
||
- именно так и рождается путаница "почему у меня есть lead queue"
|
||
|
||
Выбор:
|
||
|
||
- для rollout брать **вариант 1**
|
||
- отдельную per-tool policy layer обсуждать только если позже реально появятся mixed-access tools внутри одной semantic group
|
||
|
||
### J. Где должна жить semantics для filtered `task_list`
|
||
|
||
**1. Dedicated controller-owned inventory method с явным allowlisted output contract**
|
||
|
||
`🎯 9 🛡️ 10 🧠 5`
|
||
|
||
Примерный объём: `50-110` строк изменений
|
||
|
||
Суть:
|
||
|
||
- raw `tasks.listTasks()` остаётся raw/read helper
|
||
- для `task_list` вводится отдельный semantic owner:
|
||
- либо public `tasks.listTaskInventory(filters?)`
|
||
- либо internal controller helper с тем же смыслом, если public width хотят держать узкой
|
||
- output contract фиксируется как explicit inventory row allowlist, а не `slimTaskForList(...)` blocklist passthrough
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- фильтры и payload-shaping живут рядом с controller-owned queue/inventory semantics
|
||
- не приходится тихо переопределять смысл старого raw `listTasks()`
|
||
- migration к компактному output становится явной и тестируемой
|
||
|
||
**2. Перегрузить существующий `tasks.listTasks(filters?)` и постепенно менять его смысл**
|
||
|
||
`🎯 5 🛡️ 6 🧠 4`
|
||
|
||
Примерный объём: `35-90` строк изменений
|
||
|
||
Суть:
|
||
|
||
- тот же метод начинает иногда означать raw list, а иногда filtered inventory
|
||
|
||
Риск:
|
||
|
||
- имя метода начинает врать о своей семантике
|
||
- выше шанс type/runtime drift и неожиданных побочных эффектов в existing callers
|
||
|
||
**3. Оставить controller raw, а фильтрацию и shape собирать прямо в MCP tool**
|
||
|
||
`🎯 4 🛡️ 5 🧠 3`
|
||
|
||
Примерный объём: `25-70` строк изменений
|
||
|
||
Суть:
|
||
|
||
- `task_list` MCP tool сам вызывает raw `tasks.listTasks()` + kanban/review helpers и строит filtered inventory локально
|
||
|
||
Риск:
|
||
|
||
- semantics inventory начинает жить вне controller
|
||
- MCP и другие consumers почти гарантированно начнут drift'ить
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- raw `tasks.listTasks()` не надо тихо превращать в semantic inventory API
|
||
|
||
### K. Как безопасно seed'ить lead-first tool access после появления `lead_briefing`
|
||
|
||
**1. Отдельный explicit lead bootstrap seed list**
|
||
|
||
`🎯 9 🛡️ 10 🧠 4`
|
||
|
||
Примерный объём: `25-70` строк изменений
|
||
|
||
Суть:
|
||
|
||
- не переиспользовать `AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES` как proxy для lead-first surfaces
|
||
- завести отдельный explicit список initial lead bootstrap tools
|
||
- включить туда `lead_briefing` и другие реально нужные first-turn lead surfaces
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- lead bootstrap и teammate operational workflow - разные вещи
|
||
- появление lead-only tool больше не ломает initial permission seed path
|
||
- semantics разрешений становится явной, а не побочной
|
||
|
||
**2. Seed'ить лидеру все registered agent-teams tools**
|
||
|
||
`🎯 4 🛡️ 6 🧠 2`
|
||
|
||
Примерный объём: `10-30` строк изменений
|
||
|
||
Суть:
|
||
|
||
- чтобы не думать о разделении, лид получает сразу весь namespaced surface
|
||
|
||
Риск:
|
||
|
||
- слишком широкий blast radius по permissions
|
||
- теряется смысл role-scoped tool surfaces
|
||
|
||
**3. Ничего не seed'ить отдельно, положиться на runtime permission prompt**
|
||
|
||
`🎯 3 🛡️ 5 🧠 1`
|
||
|
||
Примерный объём: `5-15` строк изменений
|
||
|
||
Суть:
|
||
|
||
- `lead_briefing` может быть доступен, но первый вызов пройдёт через ad hoc permission flow
|
||
|
||
Риск:
|
||
|
||
- первый ход лида становится менее детерминированным
|
||
- prompt говорит "сначала вызови lead_briefing", а runtime может отвечать permission friction
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- bootstrap-critical lead surfaces не должны зависеть от teammate permission list случайно
|
||
|
||
### L. Как удержать agenda/inventory helpers internal при текущем `bindModule(...)`
|
||
|
||
**1. Вынести их в отдельный internal module, который controller не bind'ит**
|
||
|
||
`🎯 9 🛡️ 10 🧠 5`
|
||
|
||
Примерный объём: `40-100` строк изменений
|
||
|
||
Суть:
|
||
|
||
- agenda/inventory derivation живёт в отдельном internal helper module
|
||
- `tasks.js` импортирует его, но не ре-экспортирует целиком
|
||
- `createController()` не видит этот helper module как public API
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- accidental public export risk исчезает по самой структуре кода
|
||
- проще держать public controller contract узким
|
||
|
||
**2. Оставить helpers в `tasks.js`, но не экспортировать их**
|
||
|
||
`🎯 8 🛡️ 8 🧠 3`
|
||
|
||
Примерный объём: `20-50` строк изменений
|
||
|
||
Суть:
|
||
|
||
- helpers остаются локальными функциями файла
|
||
|
||
Риск:
|
||
|
||
- работает, но `tasks.js` и так уже перегружен разными обязанностями
|
||
- выше шанс, что позже кто-то всё же экспортнет helper "для удобства"
|
||
|
||
**3. Экспортнуть helper из `tasks.js`, но считать его internal по договорённости**
|
||
|
||
`🎯 1 🛡️ 2 🧠 1`
|
||
|
||
Примерный объём: `5-15` строк изменений
|
||
|
||
Суть:
|
||
|
||
- helper появляется в `module.exports`, но мы просто стараемся не использовать его как public API
|
||
|
||
Риск:
|
||
|
||
- при текущем `bindModule(...)` это уже не internal helper, а реальный public controller method
|
||
- это прямой путь к silent surface creep и type drift
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- вариант 2 допустим только если хотят минимальный diff и готовы жёстко держать export discipline
|
||
|
||
### M. Как harden'ить lead bootstrap readiness для `lead_briefing`
|
||
|
||
**1. Role-aware required-tool preflight после wiring stabilization**
|
||
|
||
`🎯 9 🛡️ 10 🧠 5`
|
||
|
||
Примерный объём: `40-100` строк изменений
|
||
|
||
Суть:
|
||
|
||
- после того как `lead` group, registration path и permission seed уже стабилизированы
|
||
- добавляем role-aware preflight invariant:
|
||
- teammate path требует `member_briefing`
|
||
- lead path требует `lead_briefing`
|
||
- validation делается через тот же `tools/list` / `tools/call` style readiness check
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- если prompt делает `lead_briefing` canonical first action, runtime тоже должен это уметь проверять
|
||
- исчезает класс багов "prompt уже требует tool, а launch path его ещё не гарантирует"
|
||
|
||
**2. Prompt-level canonical first call + explicit fallback until readiness proven**
|
||
|
||
`🎯 7 🛡️ 7 🧠 2`
|
||
|
||
Примерный объём: `15-40` строк изменений
|
||
|
||
Суть:
|
||
|
||
- lead prompt рекомендует `lead_briefing`
|
||
- но пока нет role-aware preflight, допускается fallback на inventory path при tool/permission failure
|
||
|
||
Риск:
|
||
|
||
- migration мягче, но менее детерминированна
|
||
- остаётся окно, где canonical surface ещё не является hard runtime invariant
|
||
|
||
**3. Ничего не проверять дополнительно и надеяться на registration/permissions**
|
||
|
||
`🎯 2 🛡️ 4 🧠 1`
|
||
|
||
Примерный объём: `5-15` строк изменений
|
||
|
||
Суть:
|
||
|
||
- считаем, что если tool зарегистрирован где-то в системе, этого достаточно
|
||
|
||
Риск:
|
||
|
||
- код уже показывает, что explicit MCP readiness gate сейчас существует только для `member_briefing`
|
||
- значит для lead path это просто wishful thinking, а не контракт
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- до его внедрения prompt не должен делать `lead_briefing` hard bootstrap blocker без fallback
|
||
|
||
### N. Где должен жить source of truth для lead bootstrap tool list
|
||
|
||
**1. Exported controller/package constant рядом с existing teammate constants**
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Примерный объём: `20-50` строк изменений
|
||
|
||
Суть:
|
||
|
||
- если нужен lead bootstrap tool list, он живёт не локально в `TeamProvisioningService`
|
||
- а экспортируется из `mcpToolCatalog` рядом с:
|
||
- `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- `AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- например как:
|
||
- `AGENT_TEAMS_LEAD_BOOTSTRAP_TOOL_NAMES`
|
||
- `AGENT_TEAMS_NAMESPACED_LEAD_BOOTSTRAP_TOOL_NAMES`
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- bootstrap runtime не получает второй ручной список tool names
|
||
- catalog/permissions/prompts/tests смотрят в один и тот же source of truth
|
||
- migration для `lead_briefing` не размазывает naming policy по нескольким слоям
|
||
|
||
**2. Локальный список прямо в `TeamProvisioningService`**
|
||
|
||
`🎯 4 🛡️ 5 🧠 2`
|
||
|
||
Примерный объём: `10-30` строк изменений
|
||
|
||
Суть:
|
||
|
||
- просто прописать lead bootstrap tools строками в runtime service
|
||
|
||
Риск:
|
||
|
||
- это создаёт второй source of truth рядом с catalog
|
||
- при следующем tool rename или regrouping drift почти неизбежен
|
||
|
||
**3. Вычислять список динамически из `AGENT_TEAMS_MCP_TOOL_GROUPS` по эвристике**
|
||
|
||
`🎯 5 🛡️ 6 🧠 5`
|
||
|
||
Примерный объём: `20-60` строк изменений
|
||
|
||
Суть:
|
||
|
||
- runtime сам выводит lead bootstrap tools из group metadata или naming conventions
|
||
|
||
Риск:
|
||
|
||
- слишком неявно для bootstrap-critical path
|
||
- эвристика потом будет спорить с реальным prompt/runtime contract
|
||
|
||
Выбор:
|
||
|
||
- брать **вариант 1**
|
||
- bootstrap-critical tool lists должны экспортироваться явно, а не вычисляться по косвенным признакам
|
||
|
||
---
|
||
|
||
## 3.3 Decision Freeze For Phase 0/1
|
||
|
||
Чтобы rollout не расползся и агенты не получили смесь полу-готовых правил, для Phase 0/1 фиксируем жёсткие ограничения:
|
||
|
||
- per-task `kanban reviewer` не участвует в routing, пока write-path не начнёт реально его поддерживать
|
||
- `kanban.reviewers[]` трактуется только как reviewer pool / availability list, а не assignment на конкретную задачу
|
||
- clarification в routing contract живёт через explicit clear, а не через implicit "кто-то ответил"
|
||
- board lock в Phase 0 обязан покрыть controller agenda snapshot и multi-file board mutations, но не обязан сразу заменять все main-side raw readers
|
||
- queue roster использует controller-owned normalization c явно зафиксированными тестами, а не ad hoc смесь controller/UI эвристик
|
||
- `lead_briefing` в Phase 1 остаётся role-scoped surface без обязательного `leadName` параметра
|
||
- `lead_briefing` в catalog живёт в отдельной non-teammate-operational group, а не прячется внутри `task` group
|
||
- delivery уведомлений не входит в rollback boundary board-state mutation; board commit и message send надо разводить
|
||
- новая tool group не считается введённой, пока у неё нет явного registration path без duplicate registrations
|
||
- lead-first bootstrap не зависит только от teammate operational permission seed list
|
||
- source of truth для lead bootstrap tools экспортируется явно, а не живёт локальным списком в runtime service
|
||
- availability `lead_briefing` и permission seed для него трактуются как разные rollout concerns
|
||
- canonical first-turn `lead_briefing` не становится hard bootstrap invariant раньше, чем у lead path появляется явная readiness validation или безопасный fallback
|
||
- agenda/inventory helpers не утекают в public controller surface через случайный `module.exports`
|
||
- filtered `task_list` не должен вечно оставаться MCP-local blocklist wrapper над raw `tasks.listTasks()`
|
||
- generic board snapshot helper не становится public API, пока у него нет второго доказанного consumer
|
||
|
||
---
|
||
|
||
## 3.4 Concrete Migration Blockers Found In Code
|
||
|
||
Это не теоретические риски, а уже существующие места, которые диктуют rollout order.
|
||
|
||
### A. Lead prompt still teaches `task_list`
|
||
|
||
`🎯 10 🛡️ 10 🧠 2`
|
||
|
||
Факт из кода:
|
||
|
||
- `TeamProvisioningService` прямо содержит строку `List all tasks: task_list { teamName: "..." }`
|
||
|
||
Следствие:
|
||
|
||
- нельзя считать migration завершённым, пока lead prompt не будет переписан на `lead_briefing` как canonical first call
|
||
- одного нового MCP tool недостаточно
|
||
- member path сегодня жёстко валидирует именно `member_briefing`; lead path такого bootstrap gate пока не имеет
|
||
- поэтому у плана только 2 честных варианта:
|
||
- либо `lead_briefing` сначала canonical recommendation with fallback
|
||
- либо после stabilization wiring добавить явный lead-side preflight invariant
|
||
- prompt-only canonicalization без одного из этих двух путей здесь недостаточна
|
||
|
||
### B. Member path already anchors on `member_briefing` -> `task_briefing`
|
||
|
||
`🎯 10 🛡️ 10 🧠 2`
|
||
|
||
Факт из кода:
|
||
|
||
- member bootstrap сейчас жёстко валидирует наличие `member_briefing`
|
||
- member prompts уже учат использовать `task_briefing` как compact queue
|
||
|
||
Следствие:
|
||
|
||
- teammate migration почти не требует semantic renaming
|
||
- основной prompt migration blocker сейчас именно на lead path, а не на teammate path
|
||
|
||
### C. Current `task_briefing` renderer lives in `taskStore`
|
||
|
||
`🎯 9 🛡️ 9 🧠 4`
|
||
|
||
Факт из кода:
|
||
|
||
- `tasks.taskBriefing(...)` просто проксирует в `taskStore.formatTaskBriefing(...)`
|
||
|
||
Следствие:
|
||
|
||
- нельзя делать `lead_briefing` как второй независимый renderer рядом в store
|
||
- сначала нужен общий structured agenda DTO в controller-owned layer, и только потом два text renderer поверх него
|
||
|
||
### D. Local `.d.ts` shims already drift in concrete ways
|
||
|
||
`🎯 10 🛡️ 9 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `src/types/agent-teams-controller.d.ts` и `mcp-server/src/agent-teams-controller.d.ts` уже расходятся не только "вообще", а по конкретным методам и формам
|
||
- примеры:
|
||
- main shim не знает `tasks.memberBriefing(...)`
|
||
- main shim не знает `tasks.getTaskComment(...)`
|
||
- main shim не знает `review.startReview(...)`
|
||
- main shim не знает `runtime`
|
||
- `lookupMessage(...)` типизирован по-разному между shim файлами
|
||
|
||
Следствие:
|
||
|
||
- любой rollout нового agenda/helper surface обязан идти вместе с type-sync gate
|
||
- иначе документ будет обещать безопасную миграцию, а монорепа получит тихий type/runtime drift
|
||
|
||
### E. `lead_briefing` лучше, чем `lead_queue`, именно в Phase 1
|
||
|
||
`🎯 9 🛡️ 9 🧠 3`
|
||
|
||
Причина выбора:
|
||
|
||
- текущие briefing surfaces уже текстовые и хорошо ложатся в prompt workflow
|
||
- `lead_briefing` семантически ближе к уже существующим `member_briefing` и `task_briefing`
|
||
- JSON/queue-style surface можно добавить позже отдельным mode или отдельным tool, не мешая Phase 1 rollout
|
||
|
||
Итог:
|
||
|
||
- в Phase 1 canonical lead surface называем **`lead_briefing`**
|
||
- название `lead_queue` не используем как primary tool name в первой фазе
|
||
|
||
### F. Lead name inference still drifts from shared lead detection
|
||
|
||
`🎯 9 🛡️ 8 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- controller-side `inferLeadName(...)` ищет lead по ad hoc правилам и даже может упасть в `config.members[0]`
|
||
- shared `leadDetection` utilities живут по другому контракту и не должны тихо расходиться с queue semantics
|
||
|
||
Следствие:
|
||
|
||
- нельзя делать `lead_briefing` tool, который зависит от обязательного `leadName` input или от жёсткого "угадай одного lead actor" до того, как это выровнено
|
||
- role-scoped `lead_briefing` безопаснее, чем name-scoped surface
|
||
|
||
### G. Как `lead_briefing` должен резолвить lead identity
|
||
|
||
**1. Role-based lead surface without required `leadName` input**
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Примерный объём: `20-40` строк изменений
|
||
|
||
Суть:
|
||
|
||
- `lead_briefing` - это role-scoped surface
|
||
- tool не требует `leadName`
|
||
- внутри response можно вернуть display header с каноническим lead name, если он надёжно резолвится, но routing не зависит от этого имени
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- lead agenda одна на команду, а не на произвольного actor name
|
||
- не заставляет новый tool зависеть от слабого `inferLeadName(...)` как от hard requirement
|
||
- снижает шанс ложных ошибок из-за alias drift
|
||
|
||
Edge cases, которые этот выбор должен переживать без падения:
|
||
|
||
- в конфиге нет ни одного явно резолвимого lead candidate
|
||
- в конфиге несколько lead-like actors и single canonical name не выводится надёжно
|
||
- исторические task payload всё ещё содержат owner/reviewer alias `team-lead`
|
||
|
||
Во всех этих случаях:
|
||
|
||
- `lead_briefing` всё равно должен возвращать valid oversight queue
|
||
- допустим generic header уровня `Lead queue`
|
||
- ошибка из-за ambiguous lead identity здесь считается неправильным поведением Phase 1
|
||
|
||
**2. Require explicit `leadName` parameter**
|
||
|
||
`🎯 4 🛡️ 6 🧠 4`
|
||
|
||
Примерный объём: `20-50` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сделать `lead_briefing(memberNameLikeLead)` по аналогии с `task_briefing(memberName)`
|
||
|
||
Риск:
|
||
|
||
- лишняя зависимость от prompt/runtime name correctness
|
||
- появится ещё один alias-sensitive contract там, где role-based surface и так достаточен
|
||
|
||
**3. Infer lead name hard and fail if ambiguous**
|
||
|
||
`🎯 5 🛡️ 7 🧠 5`
|
||
|
||
Примерный объём: `40-80` строк изменений
|
||
|
||
Суть:
|
||
|
||
- заставить tool сначала выбрать один canonical lead name и падать при малейшей ambiguity
|
||
|
||
Риск:
|
||
|
||
- создаёт лишний bootstrap blocker для surface, которому имя лида не нужно для core routing
|
||
|
||
Выбор:
|
||
|
||
- для Phase 1 брать **вариант 1**
|
||
- отдельную canonical lead-name cleanup делать независимо от самого `lead_briefing`
|
||
|
||
### H. Public API width in Phase 0/1
|
||
|
||
**1. Keep new snapshot/agenda helpers internal, expose only user-facing surfaces**
|
||
|
||
`🎯 9 🛡️ 10 🧠 3`
|
||
|
||
Примерный объём: `20-50` строк изменений
|
||
|
||
Суть:
|
||
|
||
- structured agenda DTO и board snapshot helper живут как internal controller implementation detail
|
||
- наружу в public controller/tool surface добавляются только:
|
||
- `tasks.taskBriefing(...)`
|
||
- `tasks.leadBriefing(...)`
|
||
- расширенный `task_list(...)`
|
||
|
||
Почему это лучший вариант:
|
||
|
||
- минимальный public blast radius
|
||
- меньше type-shim drift
|
||
- проще Phase 0 acceptance и rollback
|
||
|
||
**2. Export generic `getBoardSnapshot(...)` immediately**
|
||
|
||
`🎯 5 🛡️ 7 🧠 5`
|
||
|
||
Примерный объём: `40-90` строк изменений
|
||
|
||
Риск:
|
||
|
||
- второй consumer для этого API ещё не доказан
|
||
- придётся сразу синхронно расширять runtime export surface и оба локальных `.d.ts` shims
|
||
|
||
**3. Export both generic snapshot helper and structured agenda DTO**
|
||
|
||
`🎯 3 🛡️ 5 🧠 6`
|
||
|
||
Примерный объём: `70-140` строк изменений
|
||
|
||
Риск:
|
||
|
||
- слишком раннее раскрытие внутреннего abstraction surface
|
||
- потом намного сложнее будет менять внутренний shape без downstream breakage
|
||
|
||
Выбор:
|
||
|
||
- для Phase 0/1 брать **вариант 1**
|
||
|
||
### I. `mcpToolCatalog` сейчас group-scoped и не умеет безопасно скрывать `lead_briefing` внутри `task`
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `AGENT_TEAMS_MCP_TOOL_GROUPS` определяет teammate visibility на уровне whole group через `teammateOperational`
|
||
- `task` group сейчас teammate-operational
|
||
- `lead_briefing`, если просто добавить его в `AGENT_TEAMS_TASK_TOOL_NAMES`, автоматически попадёт в `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- обе локальные `.d.ts` декларации сейчас знают только group ids:
|
||
- `task`
|
||
- `kanban`
|
||
- `review`
|
||
- `message`
|
||
- `process`
|
||
- `runtime`
|
||
- `crossTeam`
|
||
|
||
Следствие:
|
||
|
||
- rollout `lead_briefing` должен включать не только новый tool, но и отдельное catalog решение
|
||
- safest Phase 1 choice:
|
||
- новая `lead` group
|
||
- `teammateOperational: false`
|
||
- синхронное обновление обеих `AgentTeamsMcpToolGroupId` деклараций
|
||
- prompt migration без catalog migration здесь недостаточна
|
||
|
||
### J. Board lock в controller path ещё не существует как реальный board primitive
|
||
|
||
`🎯 10 🛡️ 9 🧠 4`
|
||
|
||
Факт из кода:
|
||
|
||
- task/kanban write paths сегодня не обёрнуты в общий board lock
|
||
- существующий `withFileLockSync(...)` живёт отдельно и используется не board path'ом, а cross-team inbox/outbox flow
|
||
- сам primitive синхронный, busy-wait и имеет фиксированные таймауты `5s` acquire / `30s` stale
|
||
|
||
Следствие:
|
||
|
||
- нельзя писать в плане просто "reuse existing lock" и считать, что board consistency уже почти есть
|
||
- нужен новый явный board contract:
|
||
- один lock на team board scope
|
||
- без silent unlocked fallback
|
||
- без ложного обещания, что low-level writes уже сериализованы сами по себе
|
||
|
||
### K. `review_request` сейчас может оставить history/kanban drift при ошибке уведомления
|
||
|
||
`🎯 10 🛡️ 10 🧠 4`
|
||
|
||
Факт из кода:
|
||
|
||
- `review_request(...)` делает `kanban.set -> task.history/reviewState update -> message send`
|
||
- если `message send` падает, catch делает только `kanban.clear`
|
||
- уже записанный `review_requested` history event не откатывается
|
||
|
||
Следствие:
|
||
|
||
- Phase 0 не должен строиться на предположении, что текущий rollback у review flow transactionally чистый
|
||
- safest fix:
|
||
- board mutation commit считать отдельной фазой
|
||
- уведомления отправлять post-commit как best-effort side effect
|
||
- queue semantics не должна зависеть от успеха inbox delivery
|
||
|
||
### L. Main-side compatibility helpers уже расходятся с queue-safe reviewer contract
|
||
|
||
`🎯 9 🛡️ 9 🧠 4`
|
||
|
||
Факт из кода:
|
||
|
||
- `TeamDataService.attachKanbanCompatibility(...)` берёт `reviewState` из persisted field
|
||
- `reviewer` там резолвится как `kanbanTaskState.reviewer ?? history(review_approved|review_started|review_requested)`
|
||
|
||
Следствие:
|
||
|
||
- agenda rollout нельзя строить через reuse этих compatibility semantics
|
||
- current-cycle queue resolver должен жить отдельно и явно
|
||
- если потом main/UI захочет тот же semantic surface, он должен звать официальный queue/inventory contract, а не копировать старую compatibility derivation
|
||
|
||
### M. У `task_list` ещё нет настоящего backing contract для filters
|
||
|
||
`🎯 10 🛡️ 9 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- MCP `task_list` сейчас делает только:
|
||
- `controller.tasks.listTasks()`
|
||
- `.map(slimTaskForList)`
|
||
- `ControllerTaskApi.listTasks()` типизирован без filters
|
||
|
||
Следствие:
|
||
|
||
- filters/limit для `task_list` требуют не только новую zod-схему в MCP tool
|
||
- нужно отдельно выбрать semantic owner:
|
||
- dedicated controller inventory method
|
||
- или internal controller inventory helper
|
||
- иначе filtered `task_list` получится полуручным MCP-side overlay без нормального contract owner
|
||
|
||
### N. Новая MCP group требует отдельного registration wiring, а не только catalog entry
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `mcp-server/src/tools/index.ts` строит registration через `REGISTRATION_BY_GROUP[group.id]`
|
||
- если добавить новый group id без этого map entry, `registerTools()` получит `undefined register`
|
||
- если наивно направить и `task`, и `lead` group на один и тот же `registerTaskTools`, легко получить duplicate registration того же tool set
|
||
|
||
Следствие:
|
||
|
||
- rollout новой `lead` group обязан синхронно решить registration strategy
|
||
- safest path:
|
||
- либо отдельный `registerLeadTools`
|
||
- либо другой явный one-time registration design без дублирования task tools
|
||
- одного изменения `mcpToolCatalog.js` здесь недостаточно
|
||
|
||
### O. Lead permission seed path сейчас завязан на teammate operational tool set
|
||
|
||
`🎯 10 🛡️ 9 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `TeamProvisioningService` в lead bootstrap spec кладёт `permissionSeedTools` из `AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- runtime suggestion expansion и teammate settings seeding тоже завязаны на этот же список
|
||
- этот permission seed path вообще активируется только когда `skipPermissions === false`
|
||
- при `skipPermissions !== false` launch идёт через `bypassPermissions`, и availability tool'а определяется registration/runtime wiring, а не seed list
|
||
|
||
Следствие:
|
||
|
||
- если `lead_briefing` уходит в новую non-teammate group, он не попадёт в lead bootstrap seed автоматически
|
||
- prompt может требовать `lead_briefing` как first call раньше, чем runtime permission path к нему стабилизирован
|
||
- rollout должен добавить отдельный lead-first seed contract, а не надеяться, что teammate list "и так почти подходит"
|
||
- при этом в плане надо различать 2 разных вопроса:
|
||
- tool **available** at runtime
|
||
- tool **pre-allowed/seeded** in permission-enabled mode
|
||
- смешивать availability и permission seeding в один пункт нельзя, иначе rollout обещает слишком много
|
||
|
||
### Q. MCP preflight readiness сейчас зафиксирован только для `member_briefing`
|
||
|
||
`🎯 10 🛡️ 9 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `TeamProvisioningService` делает explicit `tools/list` + `tools/call` validation для `member_briefing`
|
||
- при отсутствии этого tool launch path падает как real bootstrap error
|
||
- аналогичного explicit readiness gate для `lead_briefing` сейчас нет
|
||
|
||
Следствие:
|
||
|
||
- если `lead_briefing` становится canonical first call, это пока ещё не равно hard-validated launch invariant
|
||
- Phase 1/1.5 должен выбрать один из честных путей:
|
||
- добавить role-aware required-tool preflight
|
||
- или сохранить explicit fallback до его появления
|
||
- нельзя писать в плане так, будто у lead path уже есть та же степень bootstrap гарантии, что и у teammate path
|
||
|
||
### R. Для lead bootstrap list сейчас нет exported constant, аналогичного teammate constant
|
||
|
||
`🎯 10 🛡️ 9 🧠 2`
|
||
|
||
Факт из кода:
|
||
|
||
- `mcpToolCatalog.js` экспортирует `AGENT_TEAMS_NAMESPACED_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- `TeamProvisioningService` импортирует именно этот exported constant
|
||
- аналогичного exported constant для lead bootstrap surfaces сейчас нет
|
||
|
||
Следствие:
|
||
|
||
- если lead bootstrap tools выбрать правильно, их всё равно легко положить "временным локальным массивом" в runtime service
|
||
- это создаст новый source of truth рядом с catalog и tests
|
||
- safest path:
|
||
- экспортировать dedicated lead bootstrap constants из controller package root
|
||
- и уже их использовать в runtime/bootstrap wiring
|
||
|
||
### S. Unreadable task rows сейчас silently выпадают из raw reader
|
||
|
||
`🎯 9 🛡️ 9 🧠 4`
|
||
|
||
Факт из кода:
|
||
|
||
- `taskStore.listRawTasks(...)` обходит все task JSON files
|
||
- при `normalizeTask(...)` error такая строка просто пропускается
|
||
- это значит, что malformed task row может исчезнуть из list-based views без явного сигнала
|
||
|
||
Следствие:
|
||
|
||
- derived queue не должна inherit'ить semantics "если задача не читается, притворимся, что её нет"
|
||
- особенно опасно это для lead surface, потому что board corruption превращается в silent omission
|
||
- safest Phase 0 path:
|
||
- queue-grade snapshot builder собирает `anomalies[]`
|
||
- `lead_briefing` поднимает такие случаи как repair bucket / warning summary
|
||
- `task_briefing` не делает вид, что board чист, если snapshot уже увидел unreadable rows
|
||
|
||
### P. `createController()` автоматически публикует весь `module.exports` bound module'а
|
||
|
||
`🎯 10 🛡️ 10 🧠 3`
|
||
|
||
Факт из кода:
|
||
|
||
- `controller.js` делает `bindModule(context, tasks)` поверх всего `module.exports` из `internal/tasks.js`
|
||
- значит любой новый export из `tasks.js` автоматически становится method на `controller.tasks`
|
||
|
||
Следствие:
|
||
|
||
- internal agenda/inventory helpers нельзя просто "временно экспортнуть" из `tasks.js`
|
||
- иначе public controller surface начнёт расползаться почти случайно
|
||
- safest path:
|
||
- отдельный internal helper module
|
||
- или local non-exported functions в `tasks.js`
|
||
|
||
---
|
||
|
||
## 4. Что не является целью
|
||
|
||
- не строим в Phase 1 полноценный event-sourced board engine
|
||
- не переносим source of truth в отдельный projection-файл
|
||
- не делаем сложный real-time delta protocol с первой итерации
|
||
- не пытаемся заменить `task_get` подробной очередью
|
||
- не делаем `task_list` основным рабочим API для тиммейта
|
||
|
||
---
|
||
|
||
## 5. Рассмотренные варианты
|
||
|
||
### Вариант 1. Derived agenda on read + team lock + role queues
|
||
|
||
`🎯 10 🛡️ 10 🧠 6`
|
||
|
||
Примерный объём: `250-420` строк изменений
|
||
|
||
Суть:
|
||
|
||
- не создаём persisted projection как новый durable слой
|
||
- строим derived queue прямо во время чтения
|
||
- читаем raw task/review/kanban состояние под общим team-level lock
|
||
- делаем канонические queue surfaces для лида и участника
|
||
- `task_list` превращаем в filtered inventory
|
||
|
||
Плюсы:
|
||
|
||
- минимальный риск двойного source of truth
|
||
- проще доказать корректность
|
||
- быстрее rollout
|
||
- меньше шансов получить stale queue
|
||
|
||
Минусы:
|
||
|
||
- нет built-in delta sync с первого дня
|
||
- каждое queue чтение требует пересчёта
|
||
|
||
### Вариант 2. Persisted projection sidecar с первого дня
|
||
|
||
`🎯 8 🛡️ 7 🧠 6`
|
||
|
||
Примерный объём: `320-520` строк изменений
|
||
|
||
Суть:
|
||
|
||
- при каждом mutation пересчитывать и сохранять projection-файл рядом с board state
|
||
|
||
Плюсы:
|
||
|
||
- быстрые чтения
|
||
- удобная база для future delta
|
||
|
||
Минусы:
|
||
|
||
- второй durable слой
|
||
- выше риск расхождения raw state и projection
|
||
- нужно аккуратно закрывать partial write / multi-file atomicity
|
||
|
||
### Вариант 3. Сразу agenda + delta + persisted projection
|
||
|
||
`🎯 6 🛡️ 5 🧠 8`
|
||
|
||
Примерный объём: `450-750` строк изменений
|
||
|
||
Суть:
|
||
|
||
- сразу строить полноценный snapshot/revision/delta протокол
|
||
|
||
Плюсы:
|
||
|
||
- самая "умная" архитектура на бумаге
|
||
|
||
Минусы:
|
||
|
||
- это лучший способ слишком рано усложнить систему
|
||
- высокий риск словить subtle bugs в очередях
|
||
- большая цена ошибок, потому что именно queue tool влияет на поведение агентов
|
||
|
||
### Выбор
|
||
|
||
Выбираем **Вариант 1 как Phase 1**, а delta/revision переносим в **Phase 2**.
|
||
|
||
Это даёт правильную последовательность:
|
||
|
||
1. сначала стабилизируем семантику и ownership
|
||
2. потом оптимизируем транспорт и payload
|
||
|
||
---
|
||
|
||
## 6. Архитектурный принцип
|
||
|
||
Правильный порядок слоёв такой:
|
||
|
||
1. **Raw source of truth**
|
||
- task files
|
||
- kanban-state
|
||
- review history / reviewState
|
||
|
||
2. **Derived decision layer**
|
||
- `actionOwner`
|
||
- `nextAction`
|
||
- `queueCategory`
|
||
- `watchers`
|
||
- `reasonCode`
|
||
|
||
3. **Role-based queue surfaces**
|
||
- teammate agenda
|
||
- lead agenda via `lead_briefing`
|
||
|
||
4. **Inventory/search surface**
|
||
- filtered `task_list`
|
||
|
||
5. **Full-detail surface**
|
||
- `task_get`
|
||
|
||
⚠️ Важно: политика работы должна жить в layer 2, а не в голове LLM.
|
||
|
||
---
|
||
|
||
## 7. Канонические tool surfaces
|
||
|
||
### 7.1 `member_briefing`
|
||
|
||
Назначение:
|
||
|
||
- identity
|
||
- роль
|
||
- рабочие правила
|
||
- как пользоваться board tooling
|
||
|
||
Не должно включать:
|
||
|
||
- полный живой список задач
|
||
- большие live queue payload
|
||
|
||
Причина:
|
||
|
||
- bootstrap-инструмент не должен разрастаться вместе с board state
|
||
- иначе он станет одновременно и prompt bootstrap, и live queue, и будет быстро протухать
|
||
|
||
### 7.2 `task_briefing`
|
||
|
||
Назначение:
|
||
|
||
- каноническая compact operational queue для конкретного участника
|
||
|
||
Новый смысл:
|
||
|
||
- не просто "мои assigned tasks"
|
||
- а "что мне сейчас делать и что мне важно знать"
|
||
|
||
Структура ответа:
|
||
|
||
- identity / actor
|
||
- `actionable`
|
||
- `awareness`
|
||
- компактные counters
|
||
- `revision` в future phase
|
||
|
||
⚠️ Важное уточнение после code review:
|
||
|
||
- в текущем runtime `task_briefing` возвращает текст, а не JSON
|
||
- поэтому safest Phase 0/1 path такой:
|
||
- внутри строим **structured agenda DTO**
|
||
- наружу по-прежнему рендерим компактный текст для совместимости
|
||
- JSON-variant можно добавлять позже как отдельный surface или новый output mode
|
||
|
||
Так мы не блокируем future delta/revision, но и не делаем лишний breaking change в самом начале.
|
||
|
||
Exact Phase 1 contract:
|
||
|
||
- input:
|
||
- `teamName`
|
||
- `memberName` - обязателен
|
||
- output:
|
||
- text briefing
|
||
- semantics:
|
||
- actor-specific
|
||
- primary teammate operational surface
|
||
- built from internal structured agenda DTO, а не напрямую из legacy formatter logic
|
||
|
||
Дополнительное архитектурное правило:
|
||
|
||
- `taskStore.formatTaskBriefing(...)` в нынешнем виде не должен становиться permanent semantic owner новой agenda logic
|
||
- правильный direction такой:
|
||
- controller-owned structured agenda DTO
|
||
- текстовый renderer для `task_briefing`
|
||
- текстовый renderer для `lead_briefing`
|
||
|
||
### 7.3 `lead_briefing`
|
||
|
||
Нужен отдельный lead surface.
|
||
|
||
Выбор имени для Phase 1:
|
||
|
||
- canonical tool name: `lead_briefing`
|
||
|
||
Почему не `lead_queue`:
|
||
|
||
- текущие briefings уже текстовые
|
||
- prompt ecosystem уже заточен на human-readable briefing surfaces
|
||
- это уменьшает объём breaking changes в MCP descriptions и lead prompts
|
||
- future structured/JSON variant можно добавить позже без конфликта имён
|
||
|
||
Почему не надо использовать raw `task_list` как lead queue:
|
||
|
||
- лид тоже не должен разгребать весь inventory каждый ход
|
||
- lead queue должна показывать именно:
|
||
- unassigned
|
||
- reviewer missing
|
||
- clarification needed
|
||
- dependency repair
|
||
- review routing anomalies
|
||
- waiting on user
|
||
- aggregate board pressure
|
||
|
||
Рекомендация по rollout:
|
||
|
||
- вводим новый lead-specific surface отдельно
|
||
- в первой фазе это именно `lead_briefing`, а не новый JSON queue contract
|
||
- только после этого перестаём учить lead использовать `task_list` как first stop
|
||
- не делаем silent redefinition `task_list` в тот же самый момент
|
||
|
||
Exact Phase 1 contract:
|
||
|
||
- input:
|
||
- `teamName`
|
||
- без обязательного `leadName`
|
||
- controller contract:
|
||
- `tasks.leadBriefing(): Promise<string>`
|
||
- MCP contract:
|
||
- `lead_briefing { teamName, claudeDir? }`
|
||
- output:
|
||
- text briefing
|
||
- semantics:
|
||
- role-scoped lead operational surface
|
||
- не зависит от точного caller alias
|
||
- может отображать resolved lead name в header, но routing не должен зависеть от него
|
||
- ambiguity handling:
|
||
- unique lead name найден -> можно показать его в header
|
||
- unique lead name не найден -> вернуть generic `Lead queue` header без ошибки
|
||
- catalog placement:
|
||
- отдельная `lead` group
|
||
- `teammateOperational: false`
|
||
- `lead_briefing` не должен попадать в `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
|
||
### 7.3.1 Bootstrap sequencing for `lead_briefing`
|
||
|
||
Здесь нельзя делать rollout "в любом порядке".
|
||
|
||
Безопасная последовательность такая:
|
||
|
||
1. сначала зарегистрировать `lead_briefing` как отдельный tool surface:
|
||
- новая `lead` group
|
||
- явный registration path в `mcp-server/src/tools/index.ts`
|
||
- синхронные `.d.ts` updates
|
||
2. потом экспортировать отдельный lead bootstrap tool list рядом с teammate constants
|
||
3. потом wired'ить lead permission seed path для режима, где `skipPermissions === false`
|
||
4. потом выбрать один честный runtime contract для first-turn availability:
|
||
- либо role-aware preflight hard-check
|
||
- либо явный documented fallback
|
||
5. только после этого переписывать lead prompt так, чтобы `lead_briefing` был canonical first action
|
||
|
||
No-go rule:
|
||
|
||
- нельзя сначала написать в lead prompt "сначала вызови `lead_briefing`", а потом уже разбираться, видит ли его runtime вообще
|
||
- нельзя считать `bypassPermissions` mode доказательством того, что отдельный lead bootstrap path не нужен
|
||
- availability, registration и permission seeding - это три разных слоя rollout
|
||
|
||
### 7.4 `task_list`
|
||
|
||
Новая роль:
|
||
|
||
- filtered inventory/search
|
||
- fallback browse tool
|
||
- не основной operational queue
|
||
|
||
Желаемое поведение:
|
||
|
||
- additive filters только по стабильным dimension'ам:
|
||
- `owner`
|
||
- `status`
|
||
- `reviewState`
|
||
- `kanbanColumn`
|
||
- `relatedTo`
|
||
- `blockedBy`
|
||
- `limit`
|
||
- для routine workflow агентам в prompt рекомендуем не `task_list`, а `task_briefing`
|
||
|
||
Что сознательно **не** добавляем в первой фазе:
|
||
|
||
- `member` filter
|
||
- `reviewer` filter
|
||
|
||
Причина:
|
||
|
||
- `member` слишком двусмысленный: owner, actionOwner, watcher или просто "как-то связан"
|
||
- `reviewer` в текущем runtime не является стабильным raw inventory field
|
||
- actor-centric queries должны идти через `task_briefing` и `lead_briefing`, а не через перегруженный `task_list`
|
||
|
||
Уточнение по фильтрам:
|
||
|
||
- `status` фильтрует raw `task.status`
|
||
- `reviewState` фильтрует effective review state
|
||
- `kanbanColumn` в первой фазе допустим только как alias для review overlay columns:
|
||
- `review`
|
||
- `approved`
|
||
- не надо вводить generic `column=todo|in_progress|done`, будто inventory layer уже знает full kanban model для всех задач
|
||
- `relatedTo` и `blockedBy` должны принимать обычный task ref в том же формате, что и `task_get` / `resolveTaskRef`
|
||
|
||
Правила фильтрации:
|
||
|
||
- фильтры conjunctive, а не "любой из"
|
||
- `limit` применяется после фильтрации
|
||
- отсутствие фильтров сохраняет совместимую unfiltered semantics
|
||
- filtered `task_list` не сортирует по agenda-priority и не подменяет `actionOwner`
|
||
- output остаётся slim inventory JSON, а не agenda DTO
|
||
- если filter требует kanban knowledge (`reviewState`, `kanbanColumn`), он реализуется в controller-owned inventory layer, а не через raw `taskStore.listTasks()` напрямую
|
||
|
||
Exact Phase 1 MCP contract:
|
||
|
||
- `task_list { teamName, claudeDir?, owner?, status?, reviewState?, kanbanColumn?, relatedTo?, blockedBy?, limit? }`
|
||
|
||
Exact backing rule:
|
||
|
||
- raw `tasks.listTasks()` не надо тихо переопределять как filtered inventory API
|
||
- safest path:
|
||
- dedicated controller inventory contract
|
||
- или internal controller-owned inventory helper, который является semantic owner для MCP tool
|
||
- MCP layer не должен сам становиться permanent owner review/kanban-aware filtering logic
|
||
|
||
Output-shape migration rule:
|
||
|
||
- текущий `slimTaskForList(...)` blocklist-подход приемлем только как legacy compatibility starting point
|
||
- целевой compact contract для `task_list` должен быть explicit allowlist inventory row
|
||
- migration на allowlist делается явно и с contract tests, а не тихо "когда-нибудь потом"
|
||
|
||
Public-surface rule for Phase 0/1:
|
||
|
||
- `task_briefing`
|
||
- `lead_briefing`
|
||
- `task_list`
|
||
|
||
это public MCP/controller surfaces.
|
||
|
||
А вот:
|
||
|
||
- structured agenda DTO
|
||
- generic board snapshot helper
|
||
|
||
остаются internal implementation details, пока у них не появится второй доказанный consumer.
|
||
|
||
### 7.4.1 Target inventory row for `task_list`
|
||
|
||
Чтобы migration away from blocklist была однозначной, у `task_list` нужен целевой public row contract уже в плане.
|
||
|
||
Target V1 row:
|
||
|
||
```ts
|
||
type TaskInventoryRow = {
|
||
id: string;
|
||
displayId: string;
|
||
subject: string;
|
||
status: 'pending' | 'in_progress' | 'completed' | 'deleted';
|
||
owner?: string;
|
||
reviewState: 'none' | 'review' | 'needsFix' | 'approved';
|
||
needsClarification?: 'lead' | 'user';
|
||
blockedBy?: string[];
|
||
blocks?: string[];
|
||
related?: string[];
|
||
commentCount: number;
|
||
createdAt?: string;
|
||
updatedAt?: string;
|
||
};
|
||
```
|
||
|
||
Важно:
|
||
|
||
- здесь `reviewState` - это effective inventory review state, а не слепой passthrough сырого `task.reviewState`
|
||
- inventory row deliberately не включает:
|
||
- `comments`
|
||
- `historyEvents`
|
||
- `workIntervals`
|
||
- `attachments`
|
||
- `prompt`
|
||
- `sourceMessage`
|
||
- произвольные future fields по blocklist-инерции
|
||
- allowlist должен быть закрытым и тестируемым
|
||
- если позже реально понадобится ещё одно поле, его добавляют осознанно через contract change, а не "оно само просочилось"
|
||
|
||
Уточнение по migration safety:
|
||
|
||
- в раннем rollout лучше сначала добавить фильтры и новый `lead_briefing`
|
||
- а default unfiltered semantics `task_list` оставить совместимой, пока prompts и callers не переедут
|
||
- teammate catalog можно урезать раньше, чем глобально менять meaning самого инструмента
|
||
|
||
### 7.5 `task_get`
|
||
|
||
Роль остаётся прежней:
|
||
|
||
- полный контекст одной конкретной задачи
|
||
- history/comments/dependencies/files/clarifications
|
||
|
||
Новая практика:
|
||
|
||
- queue surface выдаёт компактные карточки и ссылки на task ids
|
||
- детали читаются через `task_get`
|
||
|
||
---
|
||
|
||
## 8. Derived read model
|
||
|
||
### 8.1 Raw входы
|
||
|
||
Derived layer должен смотреть на:
|
||
|
||
- `task.status`
|
||
- `task.owner`
|
||
- `task.dependencies`
|
||
- `task.reviewState`
|
||
- history events по review / status / clarification
|
||
- kanban column
|
||
- reviewer pool availability
|
||
- runtime identity actor
|
||
- queue roster snapshot
|
||
|
||
### 8.1.1 History-derived vs field-derived signals
|
||
|
||
Это важное уточнение после code review.
|
||
|
||
Не все workflow-сигналы в текущей системе одинаково "историчны":
|
||
|
||
- **history-derived**
|
||
- review state
|
||
- review cycle transitions
|
||
- work status transitions
|
||
- **field-derived**
|
||
- current owner
|
||
- current `needsClarification`
|
||
- current dependency lists
|
||
- current related links
|
||
|
||
Практическое следствие:
|
||
|
||
- review resolver можно и нужно строить на `historyEvents`
|
||
- owner/clarification resolver нельзя делать так, будто у него такой же append-only history contract внутри task JSON
|
||
- task logs и transcript activity могут быть полезны для диагностики, но не должны становиться required input для queue derivation в Phase 0/1
|
||
- actor identity normalization обязательна:
|
||
- реальное имя лида
|
||
- `team-lead`
|
||
должны считаться одним logical actor там, где речь идёт о valid ownership/reply semantics
|
||
- roster validity должна проверяться по одной канонической queue-side нормализации, а не по смешению controller и UI resolver heuristics
|
||
|
||
Ещё одно важное следствие:
|
||
|
||
- queue semantics и stall-monitor semantics не обязаны совпадать 1-в-1
|
||
- для queue review может быть actionable уже с `review_requested`
|
||
- для stall-monitor "started review" может начинаться только с `review_started`
|
||
|
||
Это не конфликт, а разные вопросы:
|
||
|
||
- queue отвечает "кто должен сделать следующий шаг"
|
||
- stall-monitor отвечает "есть ли доказательство, что review реально начали"
|
||
|
||
### 8.1.2 Hard exclusions for Phase 0/1
|
||
|
||
Чтобы derived queue не строилась на сигналах, которые код сегодня не поддерживает как надёжные:
|
||
|
||
- не используем `kanban-state.tasks[taskId].reviewer` для routing
|
||
- не используем `kanban.reviewers[]` как assignment конкретного reviewer
|
||
- не используем task logs / transcript activity как required input
|
||
- не выводим mandatory review из free-form role текста вроде `reviewer`, `qa`, `tech-lead`
|
||
|
||
### 8.1.3 Signal trust matrix
|
||
|
||
Важное правило: derived queue не "усредняет" все сигналы. У каждого сигнала должен быть свой trust level.
|
||
|
||
| Signal | Role in queue | Trust level in Phase 0/1 |
|
||
| --- | --- | --- |
|
||
| `historyEvents.review_*` текущего цикла | active review routing | authoritative |
|
||
| `task.reviewState` | fallback review signal | advisory fallback |
|
||
| `kanban-state.tasks[taskId].column` | overlay hint for review/approved | advisory fallback |
|
||
| `task.owner` | current accountable implementer | authoritative after roster normalization |
|
||
| `task.needsClarification` | current wait reason | authoritative after explicit-clear hardening |
|
||
| `task.blockedBy` / `blocks` / `related` | current dependency graph | authoritative but target refs must be revalidated |
|
||
| `kanban.reviewers[]` | reviewer pool / candidate set | advisory only, never per-task routing |
|
||
| `kanban-state.tasks[taskId].reviewer` | per-task reviewer | forbidden for routing in Phase 0/1 |
|
||
| task logs / transcript activity | diagnostics | debug-only |
|
||
|
||
Практическое правило:
|
||
|
||
- если два сигнала противоречат друг другу, выигрывает более trusted source
|
||
- weaker signal можно использовать только как fallback, но не как "доказательство против" stronger signal
|
||
|
||
### 8.1.4 Conflict resolution when sources disagree
|
||
|
||
Ниже не просто примеры, а конкретные implementation guardrails:
|
||
|
||
| Conflict | Winner | Why |
|
||
| --- | --- | --- |
|
||
| `historyEvents` говорит, что review активен, а `task.reviewState === none` | history | review cycle уже durable в истории |
|
||
| `review_approved` или `review_changes_requested` уже записан, но kanban всё ещё выглядит как `review` | history | stale kanban overlay не должен держать review открытым |
|
||
| `task.reviewState === review`, но current-cycle reviewer не резолвится | lead `assign_reviewer` | open review без валидного reviewer хуже, чем false confident routing |
|
||
| `needsClarification` выставлен и одновременно есть owner/reviewer routing | clarification | вопрос/блокер сильнее обычного execution flow |
|
||
| owner невалиден, но review reviewer валиден | lead `assign_owner` | потерян accountable owner, Phase 0/1 чинит ownership раньше обычного routing |
|
||
| `requestReview` durable commit прошёл, а notification send упал | committed board state | queue truth не должна зависеть от доставки сообщения |
|
||
|
||
Дополнительный guardrail:
|
||
|
||
- derived queue не пытается "склеивать" conflicting truth из `task.reviewState` и `kanban` в новый synthetic state
|
||
- если resolver не может безопасно доказать ownership/reviewer path, он обязан деградировать в lead repair bucket
|
||
|
||
### 8.2 Derived поля
|
||
|
||
Минимальный набор:
|
||
|
||
```ts
|
||
type DerivedActionOwner =
|
||
| { kind: 'member'; memberName: string }
|
||
| { kind: 'lead' }
|
||
| { kind: 'user' }
|
||
| { kind: 'none' };
|
||
|
||
type DerivedNextAction =
|
||
| 'execute'
|
||
| 'review'
|
||
| 'apply_changes'
|
||
| 'assign_owner'
|
||
| 'assign_reviewer'
|
||
| 'clarify_with_user'
|
||
| 'clarify_with_lead'
|
||
| 'repair_dependencies'
|
||
| 'wait_dependency'
|
||
| 'wait_review'
|
||
| 'none';
|
||
|
||
type DerivedQueueCategory = 'actionable' | 'waiting' | 'oversight' | 'done';
|
||
```
|
||
|
||
### 8.2.1 Minimal internal agenda DTO for Phase 0/1
|
||
|
||
Чтобы text renderers не собирали семантику каждый по-своему, у внутреннего DTO должен быть один минимальный контракт:
|
||
|
||
```ts
|
||
type AgendaItem = {
|
||
taskId: string;
|
||
displayId: string;
|
||
subject: string;
|
||
status: 'pending' | 'in_progress' | 'completed' | 'deleted';
|
||
reviewState: 'none' | 'review' | 'needsFix' | 'approved';
|
||
actionOwner: DerivedActionOwner;
|
||
nextAction: DerivedNextAction;
|
||
queueCategory: DerivedQueueCategory;
|
||
reasonCode: string;
|
||
owner?: string;
|
||
reviewer?: string | null;
|
||
blockedBy?: string[];
|
||
watchers?: string[];
|
||
needsClarification?: 'lead' | 'user';
|
||
lastMeaningfulEventAt?: string;
|
||
derivedFrom?: string[];
|
||
};
|
||
|
||
type AgendaAnomaly = {
|
||
code:
|
||
| 'unreadable_task'
|
||
| 'invalid_dependency_ref'
|
||
| 'invalid_owner_ref'
|
||
| 'invalid_reviewer_ref';
|
||
detail: string;
|
||
taskId?: string;
|
||
};
|
||
|
||
type AgendaSnapshot = {
|
||
actor:
|
||
| { kind: 'member'; memberName: string }
|
||
| { kind: 'lead' };
|
||
actionable: AgendaItem[];
|
||
awareness: AgendaItem[];
|
||
anomalies: AgendaAnomaly[];
|
||
counters: {
|
||
actionable: number;
|
||
awareness: number;
|
||
blocked: number;
|
||
waitingOnUser: number;
|
||
waitingOnLead: number;
|
||
reviewNeeded: number;
|
||
anomalies: number;
|
||
};
|
||
};
|
||
```
|
||
|
||
Важно:
|
||
|
||
- это internal contract для derivation + rendering
|
||
- `task_briefing` и `lead_briefing` рендерятся из него
|
||
- `task_list` из него не рендерится и не обязан совпадать с ним по shape
|
||
|
||
Дополнительно полезно:
|
||
|
||
- `reasonCode`
|
||
- `watchers`
|
||
- `relatedMemberNames`
|
||
- `blockedBy`
|
||
- `reviewer`
|
||
- `lastMeaningfulEventAt`
|
||
- `derivedFrom`
|
||
|
||
Где `derivedFrom` - это внутренний debug/verification след:
|
||
|
||
- `history_review_requested`
|
||
- `history_review_started`
|
||
- `kanban_state`
|
||
- `clarification_flag`
|
||
- `dependency_graph`
|
||
- `owner_status`
|
||
|
||
Он не обязателен в финальном публичном payload, но сильно помогает тестировать resolver и объяснять спорные queue decisions.
|
||
|
||
Ограничение по `lastMeaningfulEventAt`:
|
||
|
||
- это поле нельзя делать опорой для routing
|
||
- его можно использовать для sorting / summaries / tie-breaks
|
||
|
||
Причина:
|
||
|
||
- часть важных изменений живёт только в current task fields и `updatedAt`
|
||
- а не в полноценном append-only workflow event stream
|
||
|
||
### 8.2.2 `reasonCode` should use a closed v1 taxonomy
|
||
|
||
Если `reasonCode` оставить "любой строкой", resolver и renderer очень быстро начнут drift'ить.
|
||
|
||
Минимальный закрытый v1 набор лучше зафиксировать сразу:
|
||
|
||
- `waiting_user_clarification`
|
||
- `waiting_lead_clarification`
|
||
- `owner_missing`
|
||
- `owner_invalid`
|
||
- `review_reviewer_missing`
|
||
- `review_requested_waiting_pickup`
|
||
- `review_in_progress`
|
||
- `dependency_broken`
|
||
- `dependency_waiting`
|
||
- `needs_fix`
|
||
- `owner_executing`
|
||
- `owner_ready`
|
||
- `completed_no_followup`
|
||
- `terminal_approved`
|
||
- `terminal_deleted`
|
||
- `anomaly_unreadable_task`
|
||
|
||
Это не значит, что список никогда не расширится.
|
||
|
||
Это значит:
|
||
|
||
- расширение должно быть явным
|
||
- snapshot tests и text renderers должны работать от одной allowlist reason codes
|
||
- новая строка reasonCode считается contract change, а не случайной внутренней деталью
|
||
|
||
### 8.3 Что такое `actionOwner`
|
||
|
||
`actionOwner` - это **один primary actor**, который должен сделать следующий meaningful workflow шаг.
|
||
|
||
Это не:
|
||
|
||
- просто owner
|
||
- просто reviewer
|
||
- просто последний писавший комментарий
|
||
|
||
Это именно ответ на вопрос:
|
||
|
||
> кто сейчас должен сдвинуть задачу вперёд
|
||
|
||
### 8.4 Что такое `watchers`
|
||
|
||
`watchers` - это **не primary routing**, а вспомогательная visibility-модель.
|
||
|
||
Их роль:
|
||
|
||
- awareness
|
||
- summaries
|
||
- future notifications
|
||
- optional context in lead queue
|
||
|
||
Их нельзя использовать как главный способ строить operational queue, иначе лид или участник снова начнут видеть почти всё подряд.
|
||
|
||
---
|
||
|
||
## 9. Базовый precedence для `actionOwner`
|
||
|
||
Ниже канонический порядок принятия решения. Более раннее правило сильнее позднего.
|
||
|
||
### 9.1 Terminal
|
||
|
||
Если задача:
|
||
|
||
- deleted
|
||
- approved
|
||
- окончательно завершена без ожидаемого follow-up
|
||
|
||
Тогда:
|
||
|
||
- `actionOwner = none`
|
||
- `nextAction = none`
|
||
- в operational queue не попадает
|
||
- остаётся доступной через inventory/search
|
||
|
||
### 9.2 Clarification from user
|
||
|
||
Если задача явно ждёт ответа от пользователя:
|
||
|
||
- `actionOwner = user`
|
||
- `nextAction = clarify_with_user`
|
||
- лид получает oversight item
|
||
- owner получает awareness item, если задача его
|
||
|
||
### 9.3 Clarification from lead
|
||
|
||
Если задача ждёт решения/уточнения от лида:
|
||
|
||
- `actionOwner = lead`
|
||
- `nextAction = clarify_with_lead`
|
||
|
||
Phase 0 guardrail:
|
||
|
||
- пока clarification semantics стабилизированы через explicit clear, комментарий лида сам по себе не считается достаточным для снятия wait-state
|
||
- это сознательная консервативность: лучше лишний lead oversight item, чем скрытый неочищенный blocker
|
||
|
||
### 9.4 Invalid owner
|
||
|
||
Если owner отсутствует, пустой, удалён или не резолвится в roster:
|
||
|
||
- `actionOwner = lead`
|
||
- `nextAction = assign_owner`
|
||
|
||
Уточнение:
|
||
|
||
- `owner === "team-lead"` нельзя автоматически считать invalid
|
||
- alias `team-lead` и canonical lead name должны проходить через одну lead-normalization логику
|
||
|
||
### 9.5 Review requested, reviewer unresolved
|
||
|
||
Если задача ушла в review, но reviewer не удалось надёжно определить:
|
||
|
||
- `actionOwner = lead`
|
||
- `nextAction = assign_reviewer`
|
||
|
||
### 9.6 Review requested, reviewer resolved
|
||
|
||
Если review активен и reviewer валиден:
|
||
|
||
- `actionOwner = member(reviewer)`
|
||
- `nextAction = review`
|
||
- owner получает awareness item `wait_review`
|
||
|
||
Дополнительная проверка валидности reviewer обязательна:
|
||
|
||
- если reviewer помечен как removed member
|
||
- или reviewer больше не резолвится в текущий roster
|
||
|
||
то задача должна деградировать в:
|
||
|
||
- `actionOwner = lead`
|
||
- `nextAction = assign_reviewer`
|
||
|
||
Уточнение:
|
||
|
||
- reviewer нельзя выводить только из `kanban-state.reviewers[0]` как будто это стабильное назначение на конкретную задачу
|
||
- для queue нужен отдельный resolver активного reviewer именно для **текущего review cycle**
|
||
- safest precedence:
|
||
- latest `review_started.actor` внутри текущего review cycle
|
||
- latest `review_request.reviewer` внутри текущего review cycle
|
||
- иначе unresolved -> `assign_reviewer`
|
||
|
||
Здесь есть важное ужесточение после повторного code review:
|
||
|
||
- в Phase 0/1 **не надо** делать fallback в `kanban-state.tasks[taskId].reviewer`
|
||
- текущие write-path на входе в review всё равно пишут туда `null`
|
||
- значит такой fallback только создаст ложное ощущение надёжности, но не даст реальной пользы
|
||
|
||
Практическая реализация должна быть совместима с уже существующей history-based derivation review state:
|
||
|
||
```ts
|
||
function resolveCurrentCycleReviewer(task, validMembers) {
|
||
const events = [...(task.historyEvents ?? [])];
|
||
|
||
for (let i = events.length - 1; i >= 0; i -= 1) {
|
||
const event = events[i];
|
||
|
||
if (event.type === 'review_started' && isValidQueueMember(event.actor, validMembers)) {
|
||
return { reviewer: event.actor, source: 'history_review_started_actor' };
|
||
}
|
||
|
||
if (
|
||
event.type === 'review_requested' &&
|
||
isValidQueueMember(event.reviewer, validMembers)
|
||
) {
|
||
return { reviewer: event.reviewer, source: 'history_review_requested_reviewer' };
|
||
}
|
||
|
||
if (event.type === 'review_approved' || event.type === 'review_changes_requested') {
|
||
break;
|
||
}
|
||
|
||
if (event.type === 'status_changed' && event.to === 'in_progress') {
|
||
break;
|
||
}
|
||
|
||
if (event.type === 'task_created') {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return { reviewer: null, source: 'none' };
|
||
}
|
||
```
|
||
|
||
Консервативное правило:
|
||
|
||
- если resolver сомневается, задача идёт в lead queue как `assign_reviewer`
|
||
- лучше false positive в lead queue, чем false confident routing на не того reviewer
|
||
|
||
И ещё один важный guardrail:
|
||
|
||
- даже если в `kanban-state` schema есть поле `reviewer`, Phase 0/1 не должны считать его canonical signal
|
||
- пока официальный mutation surface не поддерживает reviewer как стабильно обновляемое per-task поле, history-first resolver остаётся единственно правильной базой
|
||
|
||
Дополнительные edge rules:
|
||
|
||
- если `review_started.actor` и `review_requested.reviewer` расходятся, выигрывает `review_started.actor` как более сильное доказательство того, кто реально взял review
|
||
- если reviewer совпадает с owner, queue не пытается "исправить" это сама - self-review сегодня не запрещён runtime-контрактом, значит это отдельная policy-задача, а не routing-эвристика
|
||
- если последний `review_requested.reviewer` или `review_started.actor` больше не проходит queue roster normalization, routing деградирует в `assign_reviewer`, а не в попытку взять "первого доступного reviewer из пула"
|
||
|
||
### 9.7 Broken dependencies
|
||
|
||
Если есть dependency на несуществующую, deleted или испорченную задачу:
|
||
|
||
- не делаем auto-unblock
|
||
- `actionOwner = lead`
|
||
- `nextAction = repair_dependencies`
|
||
|
||
### 9.8 Healthy blocking dependency
|
||
|
||
Если задача блокируется живой dependency:
|
||
|
||
- `actionOwner = none`
|
||
- `nextAction = wait_dependency`
|
||
- owner получает awareness
|
||
- лид может видеть это только в aggregate summary или при stall/escalation
|
||
|
||
### 9.9 Needs fixes after review
|
||
|
||
Если был `review_request_changes` или equivalent state:
|
||
|
||
- `actionOwner = member(owner)`
|
||
- `nextAction = apply_changes`
|
||
|
||
### 9.10 In progress
|
||
|
||
Если задача реально исполняется и ничего выше не сработало:
|
||
|
||
- `actionOwner = member(owner)`
|
||
- `nextAction = execute`
|
||
|
||
### 9.11 Pending with owner
|
||
|
||
Если задача pending и готова к работе:
|
||
|
||
- `actionOwner = member(owner)`
|
||
- `nextAction = execute`
|
||
|
||
### 9.12 Completed without active review
|
||
|
||
Если задача completed, reviewer не нужен или ещё не инициирован:
|
||
|
||
- в Phase 0/1 по умолчанию `actionOwner = none`
|
||
- completed task не должна автоматически превращаться в `assign_reviewer` только из-за reviewer pool или free-form reviewer role в команде
|
||
|
||
Важно:
|
||
|
||
- пока в системе нет explicit machine-readable review policy, queue не должна придумывать обязательность review из эвристик
|
||
- review routing начинается только с явного review signal:
|
||
- `review_requested`
|
||
- `review_started`
|
||
- `reviewState === review`
|
||
- future explicit review policy field, если он когда-то появится
|
||
|
||
---
|
||
|
||
## 10. Критические edge cases
|
||
|
||
### 10.1 Self-review
|
||
|
||
Если `reviewer === owner`, это плохой routing.
|
||
|
||
Правильное поведение:
|
||
|
||
- не давать такой задаче стать нормальной review-card для owner
|
||
- отправлять её в lead queue как `assign_reviewer`
|
||
- reasonCode: `self_review_invalid`
|
||
|
||
### 10.2 Несколько reviewers
|
||
|
||
Если когда-то появится multi-review:
|
||
|
||
- Phase 1 не должен пытаться распределить actionOwner между несколькими людьми
|
||
- если активных reviewers > 1, это lead attention item, пока не появится формальная multi-review модель
|
||
|
||
Иначе будет неочевидный primary owner.
|
||
|
||
### 10.3 Missing reviewer после `review_request`
|
||
|
||
Даже если текущий runtime допускает `review_request` без reviewer, queue layer не должен делать вид, что всё нормально.
|
||
|
||
Правильное поведение:
|
||
|
||
- lead action item
|
||
- не "висит у owner"
|
||
- не "висит ни у кого без объяснения"
|
||
|
||
### 10.3.1 Previous review cycle bleed-through
|
||
|
||
Отдельный риск:
|
||
|
||
- если reviewer выводится из history слишком грубо, можно случайно подтянуть reviewer из предыдущего review cycle
|
||
|
||
Поэтому queue resolver должен быть cycle-aware:
|
||
|
||
- смотреть на последние review events
|
||
- учитывать reset после возврата задачи в `in_progress` / нового рабочего цикла
|
||
- не использовать старого approver как текущего reviewer только потому, что это последнее известное review-событие
|
||
|
||
### 10.3.2 Review requested but not started yet
|
||
|
||
Это отдельный важный sub-state.
|
||
|
||
Если:
|
||
|
||
- review уже request'нут
|
||
- reviewer валиден
|
||
- но `review_started` ещё не было
|
||
|
||
то для queue это всё равно reviewer-owned actionable item:
|
||
|
||
- `actionOwner = member(reviewer)`
|
||
- `nextAction = review`
|
||
- `reasonCode` стоит различать, например `review_requested_waiting_pickup`
|
||
|
||
Но при этом нельзя механически переносить сюда stall-monitor semantics:
|
||
|
||
- для stall policy review окно может считаться ещё не "started"
|
||
- для queue reviewer уже является тем, кто должен сделать следующий шаг
|
||
|
||
### 10.4 Missing/deleted dependency
|
||
|
||
Это не wait-state. Это broken workflow.
|
||
|
||
Правильное поведение:
|
||
|
||
- лид получает repair item
|
||
- owner не должен автоматически считать задачу разблокированной
|
||
|
||
### 10.5 Комментарий на completed/review/approved задаче
|
||
|
||
Новый комментарий сам по себе не меняет `actionOwner`.
|
||
|
||
Иначе будут ложные reopening-сигналы.
|
||
|
||
Комментарий может:
|
||
|
||
- обновить `lastMeaningfulEventAt`
|
||
- появиться в awareness
|
||
|
||
Но не должен автоматически переводить задачу в actionable queue другого актёра.
|
||
|
||
### 10.6 Owner changed mid-review
|
||
|
||
Если owner поменяли, пока review активен:
|
||
|
||
- actionOwner остаётся reviewer, пока review открыт
|
||
- новый owner видит awareness
|
||
- после `review_request_changes` или `review_approve` уже действует обычная пост-review логика
|
||
|
||
### 10.6.1 Removed member as owner/reviewer
|
||
|
||
Если owner или reviewer указывает на участника, который уже в `removedNames`:
|
||
|
||
- queue не должна считать такого actor valid action owner
|
||
- это не waiting state, а routing repair case
|
||
|
||
Правильное поведение:
|
||
|
||
- removed owner -> lead queue `assign_owner`
|
||
- removed reviewer -> lead queue `assign_reviewer`
|
||
|
||
Причина:
|
||
|
||
- `resolveTeamMembers(...)` уже умеет исключать removed members из активного roster
|
||
- значит derived agenda должна использовать ту же реальность, а не слепо верить старому имени в task field/history
|
||
|
||
### 10.7 Runtime identity не определился
|
||
|
||
После code review это не такой большой риск для `task_briefing`, как казалось сначала, потому что текущий MCP tool уже требует явный `memberName`.
|
||
|
||
Поэтому правильная формулировка такая:
|
||
|
||
- для `task_briefing` ambiguity runtime identity не должна быть primary problem
|
||
- для `member_briefing` fallback по runtime identity остаётся полезным bootstrap-path
|
||
- если появится future tool без явного `memberName`, он уже не должен молча опираться на неуверенную runtime identity
|
||
|
||
Если system не смогла надёжно вывести `memberName` там, где identity всё же нужна:
|
||
|
||
- queue tool не должен молча вернуть `No tasks`
|
||
- нужно:
|
||
- либо требовать `memberName`
|
||
- либо возвращать явную ошибку identity resolution
|
||
|
||
Тихой пустой очереди быть не должно.
|
||
|
||
### 10.8 Внешние записи мимо controller
|
||
|
||
Если кто-то пишет в raw файлы вне контроллера:
|
||
|
||
- derived read path всё равно должен уметь читать tolerant snapshot
|
||
- но team-level lock гарантирует consistency только для controller-driven mutations
|
||
|
||
Это важно явно зафиксировать, чтобы не переоценить силу lock.
|
||
|
||
### 10.9 Race при multi-file update
|
||
|
||
Самый опасный баг в этой зоне:
|
||
|
||
- reader увидел уже обновлённый task
|
||
- но ещё старый kanban/review state
|
||
|
||
или наоборот.
|
||
|
||
Именно поэтому Phase 1 лучше строить вокруг:
|
||
|
||
- общего mutation coordination
|
||
- общего snapshot чтения под одним team-level lock
|
||
|
||
### 10.10 Unreadable / corrupt task row
|
||
|
||
Это отдельный failure mode, который нельзя трактовать как "задачи просто нет".
|
||
|
||
Если queue-grade reader не может нормализовать task row:
|
||
|
||
- snapshot builder должен записать anomaly
|
||
- `lead_briefing` должен показать repair/warning signal
|
||
- `task_briefing` не должен молча превращать board problem в "No tasks"
|
||
|
||
Важное уточнение:
|
||
|
||
- tolerant snapshot допустим
|
||
- silent omission - нет
|
||
|
||
Самая опасная версия бага здесь такая:
|
||
|
||
- damaged task исчезает из inventory
|
||
- никто не видит, что board уже частично испорчен
|
||
- derived queue выглядит "чище", чем реальное состояние команды
|
||
|
||
---
|
||
|
||
## 11. Как должны выглядеть очереди
|
||
|
||
### 11.1 Очередь участника
|
||
|
||
Участник должен получать не "все свои задачи", а такой shape:
|
||
|
||
```json
|
||
{
|
||
"actor": { "kind": "member", "memberName": "alice" },
|
||
"actionable": [
|
||
{
|
||
"taskId": "task-12",
|
||
"displayId": "12",
|
||
"subject": "Implement sync retries",
|
||
"actionOwner": { "kind": "member", "memberName": "alice" },
|
||
"nextAction": "execute",
|
||
"queueCategory": "actionable",
|
||
"reasonCode": "owner_ready",
|
||
"reviewer": null,
|
||
"blockedBy": []
|
||
}
|
||
],
|
||
"awareness": [
|
||
{
|
||
"taskId": "task-9",
|
||
"displayId": "9",
|
||
"subject": "API auth refactor",
|
||
"actionOwner": { "kind": "member", "memberName": "bob" },
|
||
"nextAction": "review",
|
||
"queueCategory": "waiting",
|
||
"reasonCode": "waiting_review"
|
||
}
|
||
],
|
||
"anomalies": [],
|
||
"counters": {
|
||
"actionable": 1,
|
||
"awareness": 1,
|
||
"blocked": 0,
|
||
"waitingOnUser": 0,
|
||
"waitingOnLead": 0,
|
||
"reviewNeeded": 0,
|
||
"anomalies": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
Ключевая идея:
|
||
|
||
- **actionable** должно быть коротким и операционным
|
||
- **awareness** должно быть ещё компактнее и не засорять reasoning
|
||
|
||
### 11.2 Очередь лида
|
||
|
||
Лид должен получать не full board, а priority bucket:
|
||
|
||
- needs owner assignment
|
||
- needs reviewer assignment
|
||
- needs clarification from lead
|
||
- broken dependency graph
|
||
- waiting on user
|
||
|
||
Важно:
|
||
|
||
- `stalled review / stalled work` не должны становиться primary lead bucket самого agenda resolver в Phase 0/1
|
||
- stall-сигналы можно потом добавить как отдельное summary/enrichment, если они уже приходят из существующего stall-monitor surface
|
||
|
||
Плюс summary:
|
||
|
||
- сколько задач у кого actionable
|
||
- сколько pending review
|
||
- сколько blocked
|
||
- сколько orphaned / anomalous
|
||
|
||
Лид потом уже по id прицельно открывает `task_get` или filtered `task_list`.
|
||
|
||
### 11.3 Inventory
|
||
|
||
`task_list` остаётся полезным, но только как:
|
||
|
||
- browse/search
|
||
- audit/debug
|
||
- filtered discovery
|
||
|
||
Не как "starting point for every turn".
|
||
|
||
---
|
||
|
||
## 12. Почему `watchers` нужны, но не должны править миром
|
||
|
||
Полезные watcher-кейсы:
|
||
|
||
- owner должен знать, что его задача ушла на review
|
||
- лид должен знать, что задача ждёт user input
|
||
- reviewer может быть watcher до момента formal review assignment
|
||
|
||
Но если строить queue по watchers, получится:
|
||
|
||
- лидер снова видит почти весь board
|
||
- участник получает слишком много secondary state
|
||
- LLM начинает путать "надо делать" и "полезно знать"
|
||
|
||
Поэтому правило такое:
|
||
|
||
- **routing делается по `actionOwner`**
|
||
- **watchers идут только во вторичный слой visibility**
|
||
|
||
---
|
||
|
||
## 13. Locking и consistency model
|
||
|
||
### 13.1 Что вводим
|
||
|
||
В Phase 0 вводим controller-owned team-level board lock для операций, которые меняют:
|
||
|
||
- task files
|
||
- review routing
|
||
- kanban state
|
||
- derived queue snapshot reads
|
||
|
||
Но lock должен жить на правильном уровне:
|
||
|
||
- не внутри каждой мелкой low-level функции
|
||
- а вокруг **композитных board operations**
|
||
- и вокруг queue snapshot read, который хочет увидеть консистентный cross-file state
|
||
|
||
Рекомендуемая реализация:
|
||
|
||
- новый controller-owned primitive вида `withTeamBoardLock(paths, fn)`
|
||
- lock file рядом с team state, например в `teamDir`
|
||
- reuse можно брать только как low-level идею file-based exclusivity, но не как слепое обещание, что текущий primitive уже подходит без contract hardening
|
||
|
||
После дополнительного code review это важно уточнить жёстче:
|
||
|
||
- существующий `withFileLockSync(...)` sync-only и busy-wait
|
||
- его текущие таймауты `5s` acquire / `30s` stale сами по себе ещё не являются доказанным board contract
|
||
- Phase 0 не должен молча падать обратно на unlocked read/write, если board lock не взят
|
||
|
||
Минимальный board-lock contract для плана:
|
||
|
||
- один lock на board scope команды, а не набор несвязанных task-file lock'ов
|
||
- lock timeout должен отдавать явную ошибку mutation/read caller'у, а не скрытый unlocked fallback
|
||
- lock держим только вокруг canonical board files и derived snapshot build
|
||
- уведомления и прочие non-board side effects не должны бесконечно удерживать board lock
|
||
|
||
### 13.1.1 Mutation classes and exact commit boundary
|
||
|
||
Чтобы rollout не был расплывчатым, полезно разделить board operations на 3 класса:
|
||
|
||
**Class A - routing-affecting task mutations**
|
||
|
||
- `task_set_owner`
|
||
- `task_set_status`
|
||
- `task_start`
|
||
- `task_complete`
|
||
- `task_set_clarification`
|
||
- `task_add_comment`, если комментарий меняет queue-visible state или `lastMeaningfulEventAt`
|
||
- dependency link/unlink, если оно меняет blocking graph
|
||
|
||
Правило:
|
||
|
||
- даже если durable write происходит в один task file, mutation всё равно должна проходить под board lock
|
||
- иначе queue snapshot под тем же contract не имеет единого serialization point
|
||
|
||
**Class B - multi-file board mutations**
|
||
|
||
- `review_request`
|
||
- `review_start`
|
||
- `review_approve`
|
||
- `review_request_changes`
|
||
- любые операции, которые меняют и task state, и kanban overlay, и возможно несколько task rows
|
||
|
||
Правило:
|
||
|
||
- весь board commit происходит под одним board lock
|
||
- lock release только после того, как durable board files уже записаны
|
||
|
||
**Class C - post-commit side effects**
|
||
|
||
- inbox/system notifications
|
||
- observability warnings
|
||
- expensive attachment copy/link work, если оно не влияет на queue routing
|
||
|
||
Правило:
|
||
|
||
- эти шаги идут после board commit
|
||
- они не имеют права отменять уже committed board mutation
|
||
|
||
### 13.2 Зачем
|
||
|
||
Чтобы queue read видел:
|
||
|
||
- либо старый консистентный state
|
||
- либо новый консистентный state
|
||
|
||
Но не невозможную смесь.
|
||
|
||
### 13.3 Почему этого достаточно для Phase 1
|
||
|
||
Потому что Phase 1 не добавляет второй durable projection.
|
||
|
||
Следовательно:
|
||
|
||
- нечему отдельно "протухать"
|
||
- нет projection write, который надо атомарно коммитить рядом с raw state
|
||
|
||
### 13.4 Ограничение
|
||
|
||
Если кто-то пишет в raw файлы вообще в обход controller, lock этого не предотвратит.
|
||
|
||
Но это допустимое ограничение, если:
|
||
|
||
- UI
|
||
- MCP tools
|
||
- planned task workflow
|
||
|
||
маршрутизируются через controller.
|
||
|
||
После дополнительного просмотра кода это допущение выглядит разумным:
|
||
|
||
- основные UI mutation paths уже идут через `getController(...).tasks/review/kanban`
|
||
- но это не значит, что в Phase 0 надо сразу переписать все main-side read paths под тот же lock
|
||
|
||
Отдельно важно:
|
||
|
||
- board читают не только MCP tools
|
||
- main-process сервисы вроде `TeamTaskReader`, `TeamKanbanManager` и stall-monitor snapshots тоже строят picture of truth из тех же файлов
|
||
|
||
Более правильный scope для первой фазы:
|
||
|
||
- queue projector читает state под controller-owned lock
|
||
- review/task multi-file mutations используют тот же controller-owned lock
|
||
- если позже какой-то non-controller consumer реально потребует agenda-grade snapshot, он должен идти через официальный snapshot API, а не копировать сырой read path
|
||
|
||
Иначе можно случайно раздуть rollout до общего I/O refactor и потерять фокус на агентском operational surface.
|
||
|
||
### 13.4.1 Notification boundary must be post-commit
|
||
|
||
Это отдельное ужесточение после просмотра `review_request(...)`.
|
||
|
||
Для queue correctness опасно, когда board mutation и inbox notification живут в одном pseudo-transaction, но rollback умеет откатить только часть шагов.
|
||
|
||
Phase 0 rule:
|
||
|
||
- canonical board mutation commit завершается до отправки inbox/system notifications
|
||
- notification send считается best-effort side effect
|
||
- неуспешная доставка уведомления не должна оставлять board в "откаченном наполовину" состоянии
|
||
|
||
Практический смысл:
|
||
|
||
- queue semantics не зависит от того, дошло ли служебное сообщение
|
||
- если уведомление упало, нужен warning/observability signal, а не partial rollback board state
|
||
|
||
### 13.4.2 Failure contract must be explicit
|
||
|
||
У board path должен быть не только lock, но и понятная failure semantics.
|
||
|
||
Минимальный contract:
|
||
|
||
- lock acquire timeout -> explicit retryable error, никакого unlocked fallback
|
||
- board commit failed до durable write -> mutation error, side effects не запускаются
|
||
- board commit succeeded, side effect failed -> mutation считается committed, side effect failure попадает в warning/diagnostics
|
||
- snapshot reader встретил unreadable row -> snapshot помечает anomaly, а не делает вид, что board полностью здоров
|
||
|
||
Это особенно важно для `review_request(...)`-подобных flows:
|
||
|
||
- если task/history уже committed
|
||
- а notification не ушла
|
||
|
||
то queue обязана видеть committed review state, а не fictional rollback state
|
||
|
||
---
|
||
|
||
## 13.5 Почему clarification надо стабилизировать до agenda rollout
|
||
|
||
Это отдельный guardrail, потому что здесь уже есть расхождение между "как система себя описывает" и "как она реально работает".
|
||
|
||
Сейчас:
|
||
|
||
- prompts говорят, что `needsClarification: "lead"` auto-clear'ится, когда отвечает lead
|
||
- код в task store снимает этот флаг, когда комментирует **любой не-owner автор**
|
||
|
||
Риск:
|
||
|
||
- derived queue может решить, что лидер ответил и задача больше не ждёт clarification
|
||
- хотя фактически мог прокомментировать другой teammate или reviewer
|
||
|
||
Рекомендуемая нормализация перед полноценным agenda rollout:
|
||
|
||
- **Phase 0 safe mode**
|
||
- любой clarification clear'ится только через явный `task_set_clarification clear`
|
||
- queue не верит implicit auto-clear вообще
|
||
- **Phase 1 optional ergonomics**
|
||
- после стабилизации lead identity можно вернуть:
|
||
- `lead` clarification clear на комментарий лида
|
||
- `user` clarification clear на комментарий `user`
|
||
|
||
Это важное изменение по сравнению с предыдущей версией плана:
|
||
|
||
- раньше мы пытались сразу сохранить удобство auto-clear
|
||
- теперь приоритет сдвинут в пользу надёжности и объяснимости
|
||
|
||
Практический вывод для rollout:
|
||
|
||
- в Phase 0 `needsClarification` становится надёжным routing-сигналом именно потому, что clear происходит только явно
|
||
- ambiguous clarification больше не должен зависеть от того, кто случайно оставил комментарий
|
||
|
||
Минимальный implementation note:
|
||
|
||
- Phase 0: убрать implicit clear из routing expectation и синхронно обновить prompts/briefings
|
||
- если потом возвращать ergonomic auto-clear, делать это только на controller layer, а не внутри storage
|
||
|
||
No-go rule:
|
||
|
||
- нельзя оставлять situation, где prompt обещает "lead comment closes clarification", а queue layer всё ещё зависит от текущего store behavior "любой non-owner comment clears it"
|
||
|
||
### 13.6 Почему task logs не должны становиться queue dependency в Phase 0/1
|
||
|
||
После дополнительного code review видно, что task log / transcript слой уже умеет видеть:
|
||
|
||
- `task_set_owner`
|
||
- `task_set_clarification`
|
||
- reviewer details в board activity
|
||
|
||
Но это **не** означает, что queue надо строить поверх логов.
|
||
|
||
Почему это плохая идея для первой фазы:
|
||
|
||
- логи тяжелее и дороже для read path
|
||
- это observability layer, а не canonical board state
|
||
- появится второй semantic source рядом с task/kanban state
|
||
- при расхождении будет очень сложно объяснить, почему board показывает одно, а queue решила другое
|
||
|
||
Правило для Phase 0/1:
|
||
|
||
- queue derivation строится только из canonical board state:
|
||
- task files
|
||
- kanban state
|
||
- roster resolution
|
||
- task logs допускаются только как:
|
||
- debug aid
|
||
- diagnostics
|
||
- future validation tooling
|
||
|
||
---
|
||
|
||
## 14. Phase rollout
|
||
|
||
### Phase 0 - Hardening the weak signals first
|
||
|
||
Это новая обязательная фаза после дополнительного code review.
|
||
|
||
Её цель:
|
||
|
||
- не начинать agenda rollout поверх уже известных semantic cracks
|
||
|
||
Что делаем:
|
||
|
||
1. вводим controller-owned `withTeamBoardLock(...)` / `getBoardSnapshot(...)` для agenda reads и multi-file board mutations
|
||
2. выделяем отдельный queue-grade reviewer resolver, который работает только по текущему review cycle history
|
||
3. выносим inbox/system notifications за board-state commit boundary, чтобы message delivery не ломала queue semantics частичным rollback'ом
|
||
4. в Phase 0 переводим clarification semantics в explicit-clear-only mode и синхронно обновляем prompts/briefings
|
||
5. фиксируем canonical queue roster normalization в controller layer, а не в нескольких runtime местах сразу
|
||
6. создаём внутренний structured agenda DTO + text renderers для backward-compatible `task_briefing` и нового `lead_briefing`
|
||
7. выбираем настоящий semantic owner для filtered `task_list` и не оставляем это MCP-local ad hoc логикой
|
||
8. фиксируем registration strategy для новой `lead` group, чтобы `registerTools()` не получил missing или duplicate registration path
|
||
9. экспортируем явный source of truth для lead bootstrap tool list рядом с existing teammate constants
|
||
10. добавляем отдельный lead bootstrap permission seed path для `lead_briefing` и других first-turn lead surfaces
|
||
11. выбираем lead bootstrap strategy честно:
|
||
- role-aware preflight
|
||
- или explicit fallback, пока preflight ещё не внедрён
|
||
12. держим agenda/inventory helpers вне accidental public `controller.tasks` surface
|
||
13. добавляем filters/limit в `task_list`, не ломая пока его default meaning
|
||
14. синхронизируем local `.d.ts` contracts для `agent-teams-controller`, чтобы новые exports/tools не жили только в runtime без type surface
|
||
15. явно фиксируем phase rule: no inferred mandatory review without explicit policy signal
|
||
16. переписываем lead prompt snippets в `TeamProvisioningService`, чтобы canonical first call стал `lead_briefing`, а не raw `task_list`
|
||
17. вводим queue-grade anomaly reporting, чтобы unreadable task rows не исчезали silently из board views
|
||
|
||
Критерий завершения Phase 0:
|
||
|
||
- у нас есть набор слабых сигналов, которые либо стабилизированы, либо явно помечены как unreliable
|
||
- после этого derived agenda уже можно строить на нормальной базе, а не на wishful thinking
|
||
|
||
Phase 0 exit gates:
|
||
|
||
- agenda resolver не использует per-task kanban reviewer
|
||
- review/task multi-file operations и agenda snapshot используют один controller lock contract
|
||
- board-state mutations не откатываются частично из-за ошибки inbox/system notification
|
||
- clarification больше не может тихо исчезнуть из queue из-за комментария произвольного teammate
|
||
- queue roster normalization зафиксирован тестами на `team-lead`/`lead`, removed members, external recipients и generated ids
|
||
- новая `lead` group имеет реальный MCP registration path без duplicate tool registration
|
||
- exported lead bootstrap constant существует и используется как source of truth
|
||
- lead bootstrap permission seed включает `lead_briefing`, если prompt делает его first action
|
||
- lead path либо валидирует `lead_briefing` как required tool, либо сохраняет явный fallback до такой валидации
|
||
- agenda/inventory helper не утёк в public `controller.tasks` surface случайным export'ом
|
||
- filtered `task_list` больше не висит на неопределённой MCP-only semantics
|
||
- `task_list` default unfiltered semantics остаётся совместимой, несмотря на добавление filters/limit
|
||
- lead prompt больше не рекламирует `task_list` как primary board entrypoint
|
||
- `lead_briefing` contract зафиксирован как role-scoped tool без обязательного `leadName`
|
||
- generic board snapshot helper остаётся internal implementation detail
|
||
- queue snapshot больше не может silently терять unreadable task rows без warning/anomaly signal
|
||
|
||
### Phase 1 - Semantic cleanup without persisted projection
|
||
|
||
Цель:
|
||
|
||
- правильно определить action routing
|
||
- сократить payload
|
||
- убрать путаницу между inventory и queue
|
||
|
||
Что делаем:
|
||
|
||
1. добавляем derived resolver для `actionOwner` / `nextAction` / `reasonCode`
|
||
2. строим новый queue projector поверх raw state
|
||
3. используем controller-owned team-level lock для queue snapshot и board mutations
|
||
4. перерабатываем `task_briefing` под operational agenda
|
||
5. добавляем отдельный `lead_briefing`
|
||
6. ужимаем и фильтруем `task_list`, но без слишком раннего silent semantic break
|
||
7. обновляем prompts, чтобы:
|
||
- teammates стартовали с `task_briefing`
|
||
- lead стартовал с `lead_briefing`
|
||
- `task_list` использовался только при необходимости browse/search
|
||
8. поэтапно меняем teammate operational tool catalog, чтобы `task_list` перестал быть default teammate shortcut
|
||
9. переводим `task_list` output с legacy blocklist semantics на explicit allowlisted inventory contract, когда callers уже готовы к явному переходу
|
||
10. только если когда-то появится explicit review policy, рассматриваем более сильный post-complete review routing
|
||
|
||
Ожидаемый результат:
|
||
|
||
- агент сразу видит свой реальный action set
|
||
- лид видит routing issues и pressure points, а не весь board dump
|
||
- исчезает большая часть лишней token-нагрузки
|
||
|
||
### Phase 1.5 - Compatibility and prompt hardening
|
||
|
||
Что делаем:
|
||
|
||
- сохраняем backward compatibility имени `task_briefing`
|
||
- если нужно, старый формат прячем за флагом или мягким переходом
|
||
- обновляем tool descriptions
|
||
- обновляем team provisioning instructions
|
||
- меняем lead prompts так, чтобы `lead_briefing` стал canonical first call, а `task_list` остался inventory/audit tool
|
||
- teammate prompts дополнительно поджимаем, чтобы они не тянули `task_list` без явной причины
|
||
- если role-aware lead preflight выбрали не сразу, сохраняем temporary explicit fallback path до завершения readiness hardening
|
||
- обновляем tests, которые сегодня закрепляют blocklist-semantics `task_list`, чтобы transition был явным и осознанным
|
||
|
||
### Phase 2 - Revision first, delta second
|
||
|
||
Важно: Phase 2 не должен начинаться, пока Phase 1 не покажет стабильную queue semantics.
|
||
|
||
### Phase 2A - Revision / no-change short-circuit
|
||
|
||
Добавляем:
|
||
|
||
- stable queue `revision`
|
||
- возможность ответа `unchanged`
|
||
|
||
Это уже даст экономию токенов и ускорение без сложного diff protocol.
|
||
|
||
Лучший practical path:
|
||
|
||
- повторить pattern, уже используемый в `feedRevision`
|
||
- строить `revision` как stable hash от уже нормализованного compact agenda DTO
|
||
- обязательно сортировать items детерминированно перед hashing
|
||
|
||
Что именно должно входить в revision payload:
|
||
|
||
- actor
|
||
- actionable task refs + minimal derived fields
|
||
- awareness task refs + minimal derived fields
|
||
- counters
|
||
- critical summary buckets for lead queue
|
||
|
||
Что не должно влиять на revision в первой версии:
|
||
|
||
- декоративный текст renderer
|
||
- локальные wording changes
|
||
- поля, которые не меняют queue semantics
|
||
|
||
### Phase 2B - Optional delta sync
|
||
|
||
Делать только если реально нужно по профайлингу.
|
||
|
||
Возможный контракт:
|
||
|
||
- клиент передаёт `sinceRevision`
|
||
- если сервер может корректно отдать delta, отдаёт delta
|
||
- если нет, отдает full compact queue
|
||
|
||
Правило:
|
||
|
||
- delta должен быть **оптимизацией транспорта**
|
||
- а не единственным способом понять board state
|
||
|
||
---
|
||
|
||
## 15. Миграция и backward compatibility
|
||
|
||
### Что не ломаем
|
||
|
||
- `task_get` остаётся source of full details
|
||
- `member_briefing` остаётся bootstrap tool
|
||
- старые raw task files остаются валидными
|
||
|
||
### Что меняется в поведении
|
||
|
||
- `task_briefing` перестаёт быть простым owner list
|
||
- lead перестаёт использовать full `task_list` как первую точку входа
|
||
- teammate больше не зависит от общего списка задач команды
|
||
- reviewer routing становится более явным и меньше зависит от "угадай reviewer из косвенных полей"
|
||
- clarification queue semantics в первой фазе становятся deliberately explicit, а не "магически auto-cleared"
|
||
|
||
### Совместимость
|
||
|
||
Если где-то старый runtime ещё вызывает `task_briefing` с ожиданием owner-only semantics, это обычно безопасно:
|
||
|
||
- новая agenda всё ещё покажет owned actionable items
|
||
- просто дополнительно добавит правильный awareness
|
||
|
||
`task_list` compatibility надо трактовать отдельно и жёстче:
|
||
|
||
- Phase 0/1:
|
||
- можно сохранить совместимую unfiltered semantics
|
||
- но filters/limit уже должны идти через выбранный semantic owner, а не через случайный MCP overlay
|
||
- Phase 1/1.5:
|
||
- переход к explicit allowlisted inventory row делается как осознанный contract change
|
||
- tests и prompt/docs migration должны идти в той же фазе, а не постфактум
|
||
|
||
Отдельный compatibility note:
|
||
|
||
- новые controller exports / tool names / helper contracts нельзя добавлять только в runtime код
|
||
- нужно синхронно обновлять:
|
||
- `src/types/agent-teams-controller.d.ts`
|
||
- `mcp-server/src/agent-teams-controller.d.ts`
|
||
- runtime export surface
|
||
|
||
После повторного code review здесь уже есть конкретные известные расхождения, а не абстрактный риск:
|
||
|
||
- main shim не знает `tasks.memberBriefing(...)`
|
||
- main shim не знает `tasks.getTaskComment(...)`
|
||
- main shim не знает `review.startReview(...)`
|
||
- main shim не знает `runtime`
|
||
- `lookupMessage(...)` типизирован по-разному в двух shim файлах
|
||
|
||
Именно поэтому новый public controller contract в этой задаче должен быть узким:
|
||
|
||
- добавить `tasks.leadBriefing(): Promise<string>`
|
||
- сохранить `tasks.taskBriefing(memberName): Promise<string>`
|
||
- не добавлять generic public `getBoardSnapshot(...)` в ту же фазу
|
||
|
||
Иначе получится неприятный класс ошибок:
|
||
|
||
- код работает локально в одном слое
|
||
- но типы и другой слой monorepo продолжают жить со старым контрактом
|
||
|
||
---
|
||
|
||
## 16. Что конкретно важно протестировать
|
||
|
||
### 16.1 Unit tests на resolver
|
||
|
||
Нужны table-driven тесты для сценариев:
|
||
|
||
- pending + owner
|
||
- in_progress + owner
|
||
- completed + reviewer missing
|
||
- review + reviewer resolved
|
||
- review + reviewer missing
|
||
- review + stale reviewer from previous cycle
|
||
- review requested but not started yet
|
||
- review column entry exists but per-task kanban reviewer is null
|
||
- self-review
|
||
- removed owner
|
||
- removed reviewer
|
||
- canonical lead name vs `team-lead` alias
|
||
- zero explicit lead candidates still yields valid lead queue
|
||
- multiple lead-like members do not make `lead_briefing` fail
|
||
- external inbox recipient does not become valid queue member
|
||
- generated/internal pseudo-agent id does not become valid queue member
|
||
- completed task + reviewer pool configured + no explicit review event
|
||
- waiting on user clarification
|
||
- waiting on lead clarification
|
||
- clarification cleared by wrong actor
|
||
- healthy dependency block
|
||
- broken dependency
|
||
- needsFix after review
|
||
- approved/deleted terminal
|
||
- unreadable task row produces anomaly instead of silent omission
|
||
|
||
### 16.2 Snapshot tests на queue surfaces
|
||
|
||
Проверить отдельно:
|
||
|
||
- teammate actionable/awareness
|
||
- lead action buckets
|
||
- filtered inventory outputs
|
||
- anomaly summary surfaces in lead-facing outputs when board rows are unreadable
|
||
- `lead_briefing` text output не дублирует весь board и не превращается в disguised `task_list`
|
||
- `lead_briefing` output не зависит от обязательного `leadName` input
|
||
|
||
### 16.3 Concurrency tests
|
||
|
||
Проверить:
|
||
|
||
- multi-file mutation не даёт невозможного mixed snapshot
|
||
- read под lock не ломает корректность
|
||
- stale lock cleanup не создаёт ложных успешных чтений
|
||
- board lock timeout не приводит к silent unlocked fallback
|
||
- `review_request` и `review_request_changes` не оставляют queue в промежуточном ambiguity state
|
||
- ошибка inbox/system notification после board commit не оставляет history/kanban drift
|
||
- unreadable task row не теряется silently и поднимается как anomaly
|
||
- новая `lead` group не ломает `registerTools()` и не вызывает duplicate registration существующих task tools
|
||
- exported lead bootstrap constant совпадает с реальным runtime usage path, а не дублируется локальным списком
|
||
- если lead path делает `lead_briefing` hard first action, readiness preflight действительно валидирует его наличие
|
||
- текстовый renderer `task_briefing` не расходится со structured agenda DTO
|
||
|
||
### 16.4 Prompt-path tests
|
||
|
||
Проверить, что team prompts реально подталкивают:
|
||
|
||
- member -> `task_briefing`
|
||
- lead -> `lead_briefing`
|
||
- details -> `task_get`
|
||
|
||
И отдельно зафиксировать:
|
||
|
||
- `TeamProvisioningService` больше не содержит lead hint `List all tasks: task_list ...` как primary recommendation
|
||
- новый lead hint рекомендует `lead_briefing` раньше, чем `task_list`
|
||
- если lead prompt делает `lead_briefing` hard-first, launch/preflight path тоже проверяет этот tool, а не только prompt text
|
||
|
||
### 16.5 Contract tests and type sync
|
||
|
||
Проверить:
|
||
|
||
- local `.d.ts` shim declarations синхронны с реальным runtime surface
|
||
- `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES` отражает новый teammate access policy
|
||
- `lead_briefing` явно отсутствует в `AGENT_TEAMS_TEAMMATE_OPERATIONAL_TOOL_NAMES`
|
||
- если вводится новая `lead` group, обе локальные `AgentTeamsMcpToolGroupId` декларации знают `lead`
|
||
- если выбран public controller inventory method, обе локальные `.d.ts` декларации знают и его точный contract
|
||
- у каждого `AgentTeamsMcpToolGroupId` есть валидный registration path в MCP layer
|
||
- exported lead bootstrap tool constants синхронны между runtime package surface, `.d.ts` и фактическим bootstrap usage
|
||
- lead bootstrap permission seed contract отдельно зафиксирован:
|
||
- `lead_briefing` доступен для first-turn lead flow
|
||
- seed path не зависит только от teammate operational list
|
||
- seed path трактуется отдельно от general runtime availability при `bypassPermissions`
|
||
- lead bootstrap readiness contract отдельно зафиксирован:
|
||
- teammate path hard-checks `member_briefing`
|
||
- lead path не объявляется equally strict, пока не добавлен explicit preflight или documented fallback
|
||
- accidental helper exports не появляются в `createController(...).tasks`, если они не объявлены частью public contract
|
||
- `task_list` tests меняются осознанно и явно фиксируют transition semantics, а не ломаются побочным эффектом
|
||
- queue-side roster normalization behaviour зафиксирован явно и не drift'ит незаметно относительно UI expectations
|
||
- `task_list` filter contract зафиксирован отдельно:
|
||
- only stable filters
|
||
- conjunctive semantics
|
||
- `limit` after filtering
|
||
- no hidden agenda ordering
|
||
- `task_list` output contract зафиксирован отдельно:
|
||
- explicit allowlisted inventory row as target contract
|
||
- migration away from legacy blocklist is explicit and tested
|
||
- queue anomaly contract зафиксирован отдельно:
|
||
- unreadable task rows surface as anomaly
|
||
- lead-facing outputs do not silently hide board corruption
|
||
- `TeamProvisioningService` lead prompt contract зафиксирован отдельно:
|
||
- `lead_briefing` рекомендуют раньше `task_list`
|
||
- строка `List all tasks: task_list ...` больше не используется как primary recommendation
|
||
- `lead_briefing` public contract зафиксирован отдельно:
|
||
- no required `leadName`
|
||
- text surface in Phase 1
|
||
- role-scoped semantics, not name-scoped semantics
|
||
|
||
### 16.6 Non-goals enforcement tests
|
||
|
||
Проверить:
|
||
|
||
- queue derivation не зависит от task logs/transcript activity в Phase 0/1
|
||
- owner/clarification semantics не подменяются history heuristics там, где canonical signal - это текущее task field
|
||
- queue не копирует бездумно stall-monitor правило "open review window only after review_started"
|
||
|
||
---
|
||
|
||
## 17. Признаки того, что rollout удался
|
||
|
||
### Хорошие сигналы
|
||
|
||
- lead перестал регулярно вызывать полный `task_list` для рутинной навигации
|
||
- teammate получает короткий actionable набор
|
||
- меньше "забытых" задач в review без reviewer
|
||
- меньше orphaned tasks
|
||
- меньше случаев, где LLM делает неверный workflow шаг из-за неоднозначного контекста
|
||
|
||
### Плохие сигналы
|
||
|
||
- queue стала слишком "умной" и начала скрывать важные задачи
|
||
- разные инструменты дают разное понимание одного и того же task state
|
||
- lead queue снова разрастается до pseudo-full-board
|
||
- delta/revision добавлены слишком рано и ломают простые сценарии
|
||
- clarification queue decisions всё ещё расходятся с реальным поведением board
|
||
- reviewer остаётся "магическим" и не объясняется понятным resolver precedence
|
||
|
||
---
|
||
|
||
## 17.1 Conservative Bias Rules
|
||
|
||
Если сигнал неоднозначен, queue system должна ошибаться в сторону безопасности:
|
||
|
||
1. ambiguous reviewer -> lead queue, а не speculative reviewer routing
|
||
2. ambiguous clarification clear -> lead oversight, а не скрытие wait-state
|
||
3. ambiguous dependency integrity -> repair bucket у лида, а не auto-unblock
|
||
4. ambiguous ownership -> `assign_owner`, а не молчаливая подстановка watcher/last actor
|
||
5. unreadable task row -> anomaly / lead repair signal, а не тихое исчезновение из queue
|
||
|
||
Это очень важное правило для LLM-facing surface:
|
||
|
||
- false positive в lead queue обычно терпим
|
||
- false negative в actionable queue часто приводит к реально потерянной работе
|
||
|
||
---
|
||
|
||
## 18. Чего делать не надо
|
||
|
||
### Не надо 1
|
||
|
||
Не надо сразу вводить persisted `board-projection.json` как обязательную истину.
|
||
|
||
Это красивее на схеме, но опаснее в реальности.
|
||
|
||
### Не надо 2
|
||
|
||
Не надо строить primary queue по `watchers`.
|
||
|
||
Это почти гарантированный путь обратно к информационному шуму.
|
||
|
||
### Не надо 3
|
||
|
||
Не надо считать, что фильтрованный `task_list` уже равен agenda.
|
||
|
||
Даже хороший фильтрованный inventory не заменяет явный `actionOwner`.
|
||
|
||
### Не надо 4
|
||
|
||
Не надо молча возвращать пустую очередь, если runtime identity не определился.
|
||
|
||
Такие silent failures очень дорогие.
|
||
|
||
### Не надо 5
|
||
|
||
Не надо строить Phase 1 на предположении, что текущие clarification и reviewer signals уже идеально надёжны.
|
||
|
||
Сначала их нужно harden или честно ограничить область применения.
|
||
|
||
### Не надо 6
|
||
|
||
Не надо добавлять новый queue surface только в runtime код, забыв про tool catalog, local `.d.ts` и тестовый контракт.
|
||
|
||
Для этой зоны "почти работает" особенно опасно, потому что баг всплывает не сразу и не в одном месте.
|
||
|
||
### Не надо 7
|
||
|
||
Не надо соблазняться task logs как "богаче сигналами" и тихо тащить их в основной queue resolver первой фазы.
|
||
|
||
Это почти гарантированный способ получить вторую, более дорогую и менее объяснимую source-of-truth плоскость.
|
||
|
||
### Не надо 8
|
||
|
||
Не надо делать `lead_briefing` как второй независимый renderer рядом с `taskStore.formatTaskBriefing(...)`.
|
||
|
||
Если teammate и lead surfaces будут собираться разными ad hoc formatter'ами, drift почти неизбежен.
|
||
|
||
### Не надо 9
|
||
|
||
Не надо преждевременно экспортировать generic `getBoardSnapshot(...)` как public controller API.
|
||
|
||
Пока у него нет второго доказанного consumer, это только увеличит surface area и type-sync burden.
|
||
|
||
### Не надо 10
|
||
|
||
Не надо вводить в `task_list` misleading filter вроде `column=todo|in_progress|done`, будто inventory layer уже стал полным kanban projection.
|
||
|
||
Для actor-centric и agenda-centric views есть `task_briefing` и `lead_briefing`.
|
||
|
||
### Не надо 11
|
||
|
||
Не надо класть `lead_briefing` в existing `task` group и надеяться, что prompts сами спрячут его от тиммейтов.
|
||
|
||
Если доступность инструмента определяется catalog'ом, то и ограничение должно быть в catalog, а не только в тексте подсказок.
|
||
|
||
### Не надо 12
|
||
|
||
Не надо оставлять `task_list` навсегда на blocklist-подходе только потому, что так проще пережить первую миграцию.
|
||
|
||
Иначе payload снова будет незаметно пухнуть при каждом новом task field, а inventory surface опять начнёт вести себя как полу-сырой dump.
|
||
|
||
### Не надо 13
|
||
|
||
Не надо завязывать rollback board-state mutation на успех inbox/system notification.
|
||
|
||
Если notification упала, надо сигнализировать о delivery problem, а не оставлять history и kanban в полурасходящемся состоянии.
|
||
|
||
### Не надо 14
|
||
|
||
Не надо добавлять новую `lead` group только в catalog и `.d.ts`, забыв про `mcp-server/src/tools/index.ts`.
|
||
|
||
Иначе rollout сломается не на semantics, а на missing registration path.
|
||
|
||
### Не надо 15
|
||
|
||
Не надо считать teammate operational permission seed list автоматически подходящей базой для lead-first surfaces.
|
||
|
||
Как только появляется lead-only tool вроде `lead_briefing`, это предположение перестаёт быть надёжным.
|
||
|
||
### Не надо 16
|
||
|
||
Не надо экспортить agenda/inventory helper из `internal/tasks.js` "временно", если он не должен быть public API.
|
||
|
||
При текущем `bindModule(...)` это уже не временный helper, а новый `controller.tasks.*` method.
|
||
|
||
### Не надо 17
|
||
|
||
Не надо писать в prompt, что `lead_briefing` является обязательным first step для лида, если runtime path ещё не умеет это валидировать или честно fallback'ить.
|
||
|
||
Иначе получится фальшивая bootstrap-гарантия: текст обещает одно, а launch contract её ещё не держит.
|
||
|
||
### Не надо 18
|
||
|
||
Не надо заводить lead bootstrap tool list как локальный массив в `TeamProvisioningService`, если catalog уже является source of truth для MCP surface.
|
||
|
||
Иначе первый же rename/regrouping даст тихий drift между catalog, permissions и bootstrap runtime.
|
||
|
||
### Не надо 19
|
||
|
||
Не надо inherit'ить из текущего raw reader правило "unreadable task row можно просто пропустить".
|
||
|
||
Для queue это не tolerant behavior, а скрытая потеря board truth.
|
||
|
||
---
|
||
|
||
## 19. Рекомендуемый implementation order
|
||
|
||
1. ввести controller-owned board lock primitive / snapshot API
|
||
2. вынести board notifications в post-commit best-effort path
|
||
3. перевести clarification routing в explicit-clear-only semantics и обновить prompts
|
||
4. выделить queue-grade reviewer resolver для текущего review cycle
|
||
5. формализовать queue roster normalization
|
||
6. выделить общий derived resolver `resolveTaskActionState(...)`
|
||
7. покрыть resolver table-driven тестами
|
||
8. собрать structured agenda DTO + text renderers
|
||
9. обновить `task_briefing`
|
||
10. добавить `lead_briefing`
|
||
11. завести отдельную `lead` tool group и синхронно обновить local `.d.ts` group unions
|
||
12. завести для `lead` group отдельный MCP registration path без duplicate task registrations
|
||
13. экспортировать dedicated lead bootstrap tool constants из controller package surface
|
||
14. добавить explicit lead bootstrap permission seed contract
|
||
15. выбрать lead bootstrap readiness path:
|
||
- explicit preflight
|
||
- или temporary documented fallback
|
||
16. удержать agenda/inventory helpers вне accidental public controller surface
|
||
17. выбрать backing contract для filtered `task_list`
|
||
18. добавить filters/limit в `task_list`, не ломая его default слишком рано
|
||
19. после migration readiness перевести `task_list` на allowlisted inventory row contract
|
||
20. обновить tool descriptions и provisioning prompts
|
||
21. после стабилизации добавить `revision`
|
||
22. только потом решать, нужен ли `delta`
|
||
|
||
## 19.1 Likely Change Surface
|
||
|
||
Наиболее вероятные точки изменения, если реализовывать этот план без лишнего расползания:
|
||
|
||
- `agent-teams-controller/src/internal/tasks.js`
|
||
- новый `lead_briefing` surface
|
||
- controller-level agenda snapshot orchestration
|
||
- agenda renderer wiring
|
||
- `agent-teams-controller/src/internal/agenda.js` или аналогичный internal helper module
|
||
- safest home для derived agenda/inventory helpers, которые не должны стать public controller methods автоматически
|
||
- `agent-teams-controller/src/internal/taskStore.js`
|
||
- storage-only cleanup
|
||
- legacy formatting only
|
||
- убрать из store implicit clarification policy как routing assumption
|
||
- `agent-teams-controller/src/internal/review.js`
|
||
- queue-grade reviewer resolution hooks
|
||
- board commit vs notification side-effect boundary
|
||
- при необходимости явнее фиксировать signals текущего review cycle
|
||
- `agent-teams-controller/src/internal/fileLock.js`
|
||
- база для controller-owned board lock primitive
|
||
- `agent-teams-controller/src/internal/runtimeHelpers.js`
|
||
- queue roster normalization
|
||
- lead alias normalization helpers
|
||
- `inferLeadName(...)` scope reduction so role-scoped `lead_briefing` does not depend on weak name inference
|
||
- `src/shared/utils/leadDetection.ts`
|
||
- only if controller-side lead normalization is aligned with shared rules instead of ad hoc heuristics
|
||
- `src/shared/types/team.ts`
|
||
- only if future explicit review policy is introduced
|
||
- until then, no invented review-required field in Phase 0/1
|
||
- `mcp-server/src/tools/taskTools.ts`
|
||
- новый lead tool
|
||
- optional filters/limit для `task_list`
|
||
- совместимая эволюция `task_briefing`
|
||
- явный transition away from `slimTaskForList(...)` blocklist semantics
|
||
- `mcp-server/src/tools/index.ts`
|
||
- registration wiring for new `lead` group
|
||
- защита от missing/duplicate group registration
|
||
- `agent-teams-controller/src/mcpToolCatalog.js`
|
||
- phased teammate access policy for `task_list`
|
||
- отдельная `lead` group
|
||
- registration of `lead_briefing`
|
||
- exported lead bootstrap tool constants
|
||
- `src/main/services/team/TeamProvisioningService.ts`
|
||
- prompt migration для lead/member flows
|
||
- lead bootstrap permission seed split from teammate operational seed
|
||
- role-aware MCP readiness validation or explicit fallback for `lead_briefing`
|
||
- `agent-teams-controller/src/controller.js`
|
||
- only if public bind surface needs extra guardrails around what becomes `controller.tasks.*`
|
||
- `src/types/agent-teams-controller.d.ts`
|
||
- local type shim sync for main app
|
||
- `mcp-server/src/agent-teams-controller.d.ts`
|
||
- local type shim sync for MCP package
|
||
- `mcp-server/test/tools.test.ts`
|
||
- explicit transition of `task_list` expectations
|
||
- `agent-teams-controller/test/controller.test.js`
|
||
- reviewer resolver and clarification semantics hardening
|
||
- `src/shared/utils/taskHistory.ts`
|
||
- при необходимости helper для current review cycle traversal
|
||
|
||
Принцип:
|
||
|
||
- сначала добавлять новые explicit surfaces
|
||
- только потом снижать роль старых ambiguous surfaces
|
||
|
||
---
|
||
|
||
## 20. Финальная формулировка решения
|
||
|
||
Итоговое решение, к которому пришли:
|
||
|
||
- **Phase 0:** hardening слабых сигналов (`lock`, `reviewer resolver`, `clarification semantics`, compatible renderer)
|
||
- **Phase 1:** derived projection on read как база
|
||
- **Primary routing:** через `actionOwner`
|
||
- **Secondary visibility:** через `watchers`
|
||
- **Main teammate surface:** `task_briefing`
|
||
- **Main lead surface:** отдельный `lead_briefing`
|
||
- **Search/inventory:** `task_list` с filters/limit и постепенным уходом из роли default queue
|
||
- **Inventory contract:** `task_list` идёт к explicit allowlisted `TaskInventoryRow`, а не остаётся вечным blocklist dump
|
||
- **Consistency:** controller-owned team-level lock around board mutations and queue snapshot reads
|
||
- **Reviewer source:** only current-cycle history in Phase 0/1
|
||
- **Clarification source:** explicit clear semantics first, convenience auto-clear only later if really needed
|
||
- **Signal trust:** history/task/kanban signals имеют явный priority order, queue не усредняет conflicting inputs
|
||
- **Lead contract:** `lead_briefing` role-scoped, without required `leadName`
|
||
- **Lead plumbing:** отдельная `lead` group, отдельный MCP registration path и отдельный lead bootstrap permission seed
|
||
- **Bootstrap sequencing:** lead prompt становится hard-first only after registration + seed + readiness path are real
|
||
- **Public API discipline:** snapshot/DTO helpers stay internal until a second real consumer exists
|
||
- **Export discipline:** agenda/inventory helpers не должны случайно становиться `controller.tasks.*` methods через `bindModule(...)`
|
||
- **Board anomalies:** unreadable task rows surface as anomalies, not as silent omissions
|
||
- **Phase 2:** сначала `revision`, потом только при реальной необходимости `delta`
|
||
|
||
Это самый оптимальный баланс между:
|
||
|
||
- надёжностью
|
||
- предсказуемостью для агентов
|
||
- понятностью инструментария
|
||
- умеренной сложностью rollout
|
||
|
||
Если сформулировать совсем коротко:
|
||
|
||
> Не надо учить агента самому вычислять board policy из сырых задач. Надо один раз правильно вывести `actionOwner` и отдать каждому актёру его компактную очередь.
|