mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-24 11:10:25 +02:00
* feat(autopilot): support assigning autopilot to a squad (MUL-2429) Path A (Squad-as-Leader) from the RFC: when an autopilot's assignee is a squad, dispatch resolves to squad.leader_id and executes against the leader's runtime — semantics match a human manually assigning the issue to that squad, no fan-out. Backend scope only; frontend picker change is a follow-up PR. Changes: - 096_autopilot_squad_assignee migration: drop agent FK on autopilot.assignee_id, add assignee_type column (default 'agent'), add autopilot_run.squad_id attribution column. - service.AgentReadiness: single source of truth for archived / runtime-bound / runtime-online checks. Shared by autopilot admission gate, run_only dispatch, and isSquadLeaderReady. - service.resolveAutopilotLeader: translates assignee_type/id to the agent that actually runs the work. - dispatchCreateIssue: stamps issue with assignee_type='squad' for squad autopilots and enqueues via EnqueueTaskForSquadLeader. - dispatchRunOnly: belt-and-braces readiness re-check after resolving squad → leader so a leader that went offline between admission and dispatch produces a clean failure instead of a doomed task. - handler.CreateAutopilot / UpdateAutopilot: accept assignee_type with squad/agent existence + leader-archived validation. Backward-compatible default of "agent" preserves the contract for older clients. - Analytics: AutopilotRunStarted/Completed/Failed events carry assignee_type and squad_id; PostHog can now group autopilot runs by squad without joining back to the autopilot row. Co-authored-by: multica-agent <github@multica.ai> * fix(autopilot): reject archived squads, route post-admission skips, cleanup dangling-agent autopilots (MUL-2429) Addresses three review findings on PR #2888: 1. Archived squad handling: validateAutopilotAssignee now rejects squads with archived_at set; resolveAutopilotLeader returns errSquadArchived so the admission gate fails closed; DeleteSquad now mirrors the issue transfer for autopilot rows (TransferSquadAutopilotsToLeader) so surviving autopilots flip to assignee_type='agent' (leader) instead of dangling at the archived squad. 2. dispatchRunOnly post-admission readiness: introduces errDispatchSkipped sentinel, recognised by DispatchAutopilot via handleDispatchSkip so the run is recorded as `skipped` (not `failed`). Manual triggers no longer 500 when the leader's runtime goes offline between admission and task creation. New TestManualTriggerDoesNotErrorOnPostAdmissionSkip locks the behaviour in. 3. Dangling agent assignee after migration 096 dropped the FK: shouldSkipDispatch now distinguishes pgx.ErrNoRows / errSquadArchived (hard skip — retrying won't help) from transient DB errors (fail-open). DeleteAgentRuntime pauses autopilots that target agents about to be hard-deleted (ListArchivedAgentIDsByRuntime + PauseAutopilotsByAgentAssignees) so the breakage surfaces as a paused row in the UI instead of a quiet skip-burning loop. Unit tests cover the sentinel unwrap contract and errSquadArchived errors.Is behaviour. Integration test TestAutopilotDispatchSkipsWhenRuntimeOffline re-verified against a fresh DB with migration 096 applied. Co-authored-by: multica-agent <github@multica.ai> * fix(autopilot): bump last_run_at on post-admission skip (MUL-2429) Match recordSkippedRun (pre-flight skip) and the success path so the scheduler / "last seen" UI both reflect that this tick evaluated the trigger, even when the post-admission readiness gate caught a late regression. Addresses Emacs review caveat #1 on PR #2888. Co-authored-by: multica-agent <github@multica.ai> * feat(autopilot): mixed agent/squad assignee picker in dialog (MUL-2429) End-to-end UI for assigning an autopilot to a squad. Closes the PR #2888 backend gap: the squad-as-assignee feature was already wired in Go (Path A, RFC §4) but the desktop dialog never offered the choice. - core/types/autopilot: add `AutopilotAssigneeType`, surface `assignee_type` on `Autopilot` + Create/Update request payloads. - views/autopilots/pickers/agent-picker: switch to a polymorphic AssigneeSelection (`{type, id}`); render agents and squads as two grouped sections with shared pinyin search. - views/autopilots/autopilot-dialog: maintain `assigneeType` state, send it on create/update, render the trigger avatar / hover dot with `assignee.type`. - views/autopilots/autopilots-page + autopilot-detail-page: render the assignee row using `autopilot.assignee_type` so squad-typed autopilots show the squad avatar + name, not a broken agent lookup. - locales: add `agents_group` / `squads_group` / `select_assignee` keys (en + zh-Hans), keep legacy `select_agent` for callers that still reference it. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Lambda <lambda@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
34 lines
1.7 KiB
SQL
34 lines
1.7 KiB
SQL
-- Autopilot: support assigning to a squad (MUL-2429).
|
|
--
|
|
-- Path A "Squad-as-Leader": when an autopilot's assignee is a squad, dispatch
|
|
-- still resolves to a single agent (squad.leader_id) — same semantics as a
|
|
-- human manually assigning an issue to that squad. We model this by adding an
|
|
-- assignee_type column and dropping the hard FK on assignee_id so the same
|
|
-- UUID column can reference either agent(id) or squad(id) depending on the
|
|
-- type. Referential integrity is enforced in the application layer (handler
|
|
-- validates the squad/agent is in the workspace; dispatch re-resolves at run
|
|
-- time and skip-records doomed runs instead of crashing).
|
|
|
|
ALTER TABLE autopilot
|
|
DROP CONSTRAINT IF EXISTS autopilot_assignee_id_fkey;
|
|
|
|
ALTER TABLE autopilot
|
|
ADD COLUMN assignee_type TEXT NOT NULL DEFAULT 'agent'
|
|
CHECK (assignee_type IN ('agent', 'squad'));
|
|
|
|
-- Composite index lets lookups discriminate by type cheaply, e.g. "all
|
|
-- autopilots whose assignee is squad X" without scanning the whole table.
|
|
-- The legacy idx_autopilot_assignee(assignee_id) stays for plain id lookups.
|
|
CREATE INDEX IF NOT EXISTS idx_autopilot_assignee_type_id
|
|
ON autopilot (assignee_type, assignee_id);
|
|
|
|
-- autopilot_run.squad_id: attribution hook. Populated when ap.assignee_type =
|
|
-- 'squad' so reports can group runs by squad even though the executing agent
|
|
-- (and the cost it accrues) is the leader. First version does not consume
|
|
-- the column; it exists so we never need a backfill.
|
|
ALTER TABLE autopilot_run
|
|
ADD COLUMN squad_id UUID REFERENCES squad(id) ON DELETE SET NULL;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_autopilot_run_squad_id
|
|
ON autopilot_run (squad_id) WHERE squad_id IS NOT NULL;
|