Files
multica/server/migrations/127_task_squad_id.up.sql
LinYushen 3692b6a862 fix(squad): inject leader briefing by task flag, not issue assignee (MUL-3730) (#4606)
* fix(squad): inject leader briefing by task flag, not issue assignee

Key squad-leader briefing injection off task.IsLeaderTask + task.SquadID
instead of issue.AssigneeType=='squad'. The old gate missed the most common
path — an @squad mention in a comment on an issue assigned to a plain agent
(MUL-3724) — so the leader booted with zero squad context and did the work
itself instead of orchestrating.

- migration 127: add agent_task_queue.squad_id (no FK) + partial index
- sqlc: CreateAgentTask stamps squad_id; CreateRetryTask inherits it
- service: thread squadID through EnqueueTaskForSquadLeader(+WithHandoff),
  enqueueMentionTask, and the rerun path; all 5 call sites pass the squad id
- daemon claim: unified injection keyed on leader-task + squad_id, with a
  defensive leader-identity re-check; quick-create block retained (it serves
  issue-less tasks and sets resp.SquadID/SquadName)
- briefing: strengthen leader Operating Protocol opening
- tests: claim-time injection (comment-mention/non-leader/null-squad),
  squad_id enqueue stamping, retry inheritance; existing fixture updated

Co-authored-by: multica-agent <github@multica.ai>

* test+docs(squad): dangling squad_id regression + clarify quick-create path

Address review nits on #4606:
- Add TestClaim_LeaderTaskWithDanglingSquadID_NoBriefing: squad hard-deleted
  after enqueue leaves task.squad_id dangling (no FK); claim still 200 and
  skips injection via the err!=nil guard. This is the load-bearing contract
  for dropping the FK.
- Rewrite the daemon.go injection comment to state quick-create does NOT use
  the is_leader_task/squad_id columns — it routes squad via the context JSON
  branch (qc.SquadID) and must not be folded into the column-based path.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: 魏和尚 <agent@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-06-26 16:01:33 +08:00

25 lines
1.3 KiB
SQL

-- agent_task_queue.squad_id records the squad a leader-task belongs to. The
-- daemon uses it at claim time to locate the squad whose briefing (Operating
-- Protocol + Roster + Instructions) should be injected onto the leader agent's
-- instructions, instead of inferring the squad by reverse-looking-up
-- "which squad is this agent the leader of" (which is ambiguous when one
-- agent leads multiple squads).
--
-- No FK to squad(id) on purpose: agent_task_queue is a hot, high-write task
-- queue, and we don't want squad maintenance (archive / hard-delete) to take
-- cross-table locks against it. If a squad is hard-deleted and a stale UUID
-- lingers here, the daemon's GetSquadInWorkspace lookup simply returns no row
-- and the claim path skips injection (err != nil branch) — exactly the same
-- observable behavior as "injection condition not matched". No stale briefing
-- is ever emitted.
ALTER TABLE agent_task_queue
ADD COLUMN squad_id UUID NULL;
-- Partial index over leader-task rows only: small, high hit-rate. It serves
-- admin / debug queries ("which leader tasks is this squad currently
-- running"). The daemon claim path does NOT use it — that goes through the
-- task_id primary-key path.
CREATE INDEX agent_task_queue_squad_id_idx
ON agent_task_queue (squad_id)
WHERE squad_id IS NOT NULL;