From 8fea549eaf5aa1fe7145eafbc71cfbbb451bf59c Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:05:22 +0800 Subject: [PATCH] Add built-in squads skill Co-authored-by: multica-agent --- .../builtin_skills/multica-squads/SKILL.md | 247 ++++++++++++++++++ .../references/squad-source-map.md | 185 +++++++++++++ .../internal/service/builtin_skills_test.go | 43 +++ 3 files changed, 475 insertions(+) create mode 100644 server/internal/service/builtin_skills/multica-squads/SKILL.md create mode 100644 server/internal/service/builtin_skills/multica-squads/references/squad-source-map.md diff --git a/server/internal/service/builtin_skills/multica-squads/SKILL.md b/server/internal/service/builtin_skills/multica-squads/SKILL.md new file mode 100644 index 000000000..4ae8a0c1a --- /dev/null +++ b/server/internal/service/builtin_skills/multica-squads/SKILL.md @@ -0,0 +1,247 @@ +--- +name: multica-squads +description: Use when creating, inspecting, updating, assigning, mentioning, or debugging Multica squads. Explains what squads are, squad/member fields, CLI commands, leader routing, issue assignment, comments, mentions, autopilot behavior, leader briefing, side effects, and product-gap handling. +user-invocable: false +allowed-tools: Bash(multica *) +--- + +# Multica Squads + +## Quick start + +If debugging why a squad did or did not run, inspect first: + +```bash +multica issue get --output json +multica squad get --output json +multica squad member list --output json +multica issue comment list --recent 20 --output json +``` + +If the command shape is unclear, check help instead of guessing: + +```bash +multica squad --help +multica squad member --help +multica issue update --help +multica issue comment add --help +``` + +Do not assign, comment, mention, update, delete, or record squad activity just +to test. These can mutate workspace state or trigger agent runs. + +## Core model + +A Multica squad is a workspace routing and coordination object. + +A squad is not an agent. It does not run work by itself. Current behavior: +squad-routed work runs through the squad's `leader_id` agent. + +Important consequences: + +- assigning an issue to a squad routes to the leader; +- mentioning a squad routes to the leader; +- squad-assigned autopilot resolves to the leader; +- squad members are not automatically fanned out; +- squad `instructions` are leader briefing content, not member prompts. + +## CLI + +Squad commands: + +```bash +multica squad list --output json +multica squad get --output json +multica squad create --name --leader --output json +multica squad update --instructions "" --output json +multica squad delete +``` + +Member commands: + +```bash +multica squad member list --output json +multica squad member add --member-id --type agent|member --role --output json +multica squad member remove --member-id --type agent|member +multica squad member set-role --member-id --member-type agent|member --role --output json +``` + +Squad leader evaluation command: + +```bash +multica squad activity action|no_action|failed --reason "" --output json +``` + +`activity` is a write: it records the leader's evaluation decision on an issue. +Use it only when acting as the squad leader after evaluating a trigger. + +Issue/comment commands often needed with squads: + +```bash +multica issue get --output json +multica issue update --help +multica issue comment list --output json +multica issue comment add --help +``` + +Prefer `--output json` for reads. Use `--help` before writes. + +## Squad fields + +- `id` — squad UUID. +- `workspace_id` — workspace the squad belongs to. +- `name` — display name; unique per workspace. +- `description` — human-facing metadata/display text. Do not assume runtime + prompt impact unless source proves a consumer. +- `instructions` — squad-level instructions added to the squad leader briefing. + They are not directly injected into every squad member. +- `avatar_url` — optional squad avatar URL. +- `leader_id` — agent ID of the squad leader; the runtime target for + squad-routed work. +- `creator_id` — creator of the squad. +- `archived_at` / `archived_by` — archive metadata. Archived squads are rejected + by assignment/autopilot routing paths. +- `member_count` — list response count of squad members. +- `member_preview` — list response preview of squad members. + +Use `instructions` for leader-facing coordination policy: squad responsibility, +delegation expectations, when to ask humans, and review/handoff rules. Do not +write it as if every member automatically receives it. + +## Squad member fields + +- `member_type` — `agent` or `member`. +- `member_id` — ID of the agent or workspace member. +- `role` — roster role label. Current behavior: non-empty `role` appears in the + leader briefing roster. Do not assume it creates scheduling, permissions, or + routing behavior. + +## Creation and leader membership + +Creating a squad requires `leader_id`. The leader must be a workspace agent and +cannot be archived. + +On create, the backend attempts to add the leader as a squad member with role +`leader`. When updating `leader_id`, if the new leader is not already a member, +the backend adds the new leader as a squad member with role `leader`. + +## Leader briefing + +For squad leader tasks, Multica appends a squad leader briefing to the leader +agent instructions. The briefing includes: + +- Squad Operating Protocol; +- Squad Roster; +- Squad Instructions, only when `instructions` is non-empty. + +Roster entries include member name, member type, mention markdown, and non-empty +role. Archived agent members are skipped from the briefing roster. + +## Issue assignment behavior + +Issues can be assigned to squads with: + +```text +assignee_type = "squad" +assignee_id = +``` + +Current behavior: + +- assignment routes work to `squad.leader_id`; +- it does not enqueue every squad member; +- assignment while status is `backlog` does not immediately start work; +- moving a squad-assigned issue out of `backlog` can trigger the leader; +- changing assignee cancels existing tasks for the issue before enqueueing the + new assignee path. + +Assignment validation rejects a missing type/id pair, non-existent squad, +archived squad, archived leader, and private leader when the actor cannot access +it. + +## Comment and mention behavior + +If an issue is assigned to a squad, a new comment can wake the squad leader. This +is leader routing, not member fan-out. + +Squad mention format: + +```md +[@Squad Name](mention://squad/) +``` + +Current behavior: resolve the squad, read `leader_id`, enqueue a leader task, +and use the current comment as the trigger comment. It does not enqueue every +squad member. + +## Autopilot behavior + +Autopilots can be assigned to squads. For `assignee_type = "squad"`: + +- executable agent resolves from `squad.leader_id`; +- admission/readiness checks run against the leader; +- archived squads fail closed / skip dispatch; +- run attribution records squad id where applicable. + +For `create_issue` autopilots, the created issue keeps `assignee_type = "squad"` +and `assignee_id = `, while the actual executing agent is the resolved +leader. For `run_only` autopilots, no issue is created; the task is created +directly for the resolved leader agent. + +## Handling complaints or product gaps + +When the user says squad behavior is wrong, confusing, or disappointing, do not +immediately assume code is broken and do not defend current behavior just because +it exists. Classify first: + +- expected current behavior; +- configuration issue; +- product limitation; +- actual bug. + +Explain the current source-backed behavior. If the behavior is technically +correct but product-wise bad, say so and propose a scoped product/code change. + +Do not silently change squad routing, member fan-out, leader briefing, autopilot +behavior, or comment-trigger behavior without confirmation. These are product +contract changes with side effects. + +## Side effects + +These actions can trigger agent work or mutate durable state: + +- creating a squad; +- updating squad fields; +- changing `leader_id`; +- adding/removing members; +- changing member roles; +- assigning an issue to a squad; +- moving a squad-assigned issue out of backlog; +- commenting on a squad-assigned issue; +- mentioning a squad; +- creating or triggering squad-assigned autopilots; +- recording squad activity with `multica squad activity`; +- deleting/archive squad. + +Do not perform side-effecting actions as tests unless the user explicitly +authorizes them. + +## Common wrong assumptions + +- A squad is not an agent. +- Squad work routes to `leader_id`, not every member. +- Squad mention routes to the leader, not every member. +- Squad assignment routes to the leader, not every member. +- Squad autopilot resolves to the leader as executable agent. +- `instructions` are leader briefing content, not automatic member prompts. +- `description` is not proven runtime prompt content. +- `role` is roster context, not automatic scheduling. +- Backlog assignment does not immediately start work. + +## References + +For source paths, tests, edge cases, and exact routing details, see: + +```text +references/squad-source-map.md +``` diff --git a/server/internal/service/builtin_skills/multica-squads/references/squad-source-map.md b/server/internal/service/builtin_skills/multica-squads/references/squad-source-map.md new file mode 100644 index 000000000..09444c3c9 --- /dev/null +++ b/server/internal/service/builtin_skills/multica-squads/references/squad-source-map.md @@ -0,0 +1,185 @@ +# Squad Source Map + +This file records source evidence for `multica-squads/SKILL.md`. + +Use this when the task requires exact source paths, edge-case behavior, tests, or contract verification. + +## Object Model + +### DB shape + +Source: + +```text +server/migrations/084_squad.up.sql +server/migrations/088_squad_instructions.up.sql +server/pkg/db/queries/squad.sql +packages/core/types/squad.ts +``` + +Key facts: + +- `squad` stores `name`, `description`, `leader_id`, `creator_id`, archive metadata, and `instructions`. +- `squad_member` stores `member_type`, `member_id`, and `role`. +- `member_type` is constrained to `agent` or `member`. +- issue `assignee_type` supports `squad`. + +## CLI + +Source: + +```text +server/cmd/multica/cmd_squad.go +``` + +Commands: + +```bash +multica squad list +multica squad get +multica squad create +multica squad update +multica squad delete +multica squad activity + +multica squad member list +multica squad member add +multica squad member remove +multica squad member set-role +``` + +Use `--help` for exact flags before writes. + +## Create / Update + +Source: + +```text +server/internal/handler/squad.go +``` + +Contracts: + +- create requires `leader_id`; +- leader must be workspace agent; +- archived leader is rejected; +- leader is auto-added as member with role `leader`; +- updating `leader_id` auto-adds new leader as member if missing. + +## Leader Briefing + +Source: + +```text +server/internal/handler/squad_briefing.go +server/internal/handler/daemon.go +``` + +Contracts: + +- squad leader tasks append briefing to leader agent instructions; +- briefing includes operating protocol, roster, and optional instructions; +- `instructions` section appears only when non-empty; +- archived agent members are skipped from roster; +- no traced behavior injects `instructions` into every squad member. + +## Issue Assignment + +Source: + +```text +server/internal/handler/issue.go +server/internal/handler/squad.go +server/internal/service/task.go +``` + +Contracts: + +- `assignee_type="squad"` routes to `squad.leader_id`; +- backlog assignment does not immediately enqueue; +- moving out of backlog can enqueue leader; +- assignee change cancels existing issue tasks first; +- private leader access is checked; +- pending task dedup is applied. + +## Comment / Mention + +Source: + +```text +server/internal/handler/comment.go +server/internal/handler/squad.go +server/internal/service/task.go +``` + +Contracts: + +- commenting on a squad-assigned issue can wake the leader; +- explicit `mention://squad/` resolves squad and enqueues leader; +- squad mention does not fan out to members; +- leader task uses `is_leader_task=true`; +- leader self-trigger loops are guarded. + +## Autopilot + +Source: + +```text +server/internal/service/autopilot.go +``` + +Contracts: + +- squad autopilot resolves executable agent from `squad.leader_id`; +- readiness/admission checks target the leader; +- archived squad fails closed / skips dispatch; +- `create_issue` keeps the issue assigned to the squad; +- `run_only` creates task directly for leader. + +## Child-done Parent Trigger + +Source: + +```text +server/internal/handler/issue_child_done.go +``` + +Contracts: + +- when child issue completes and parent is assigned to squad, parent squad leader can be triggered; +- routing is leader-only; +- loop guards skip same squad, same effective leader, and shared-leader cross-squad cases. + +## Private Leader Access + +Source: + +```text +server/internal/handler/agent_access.go +``` + +Contracts: + +- public leaders pass; +- agent-to-agent traffic is allowed; +- private leader access for members is limited to owner/admin or agent owner; +- system triggers are treated like agent triggers for squad leader enqueue. + +## Tests + +Relevant test groups: + +```text +server/internal/handler/squad_assign_trigger_test.go +server/internal/handler/squad_comment_trigger_test.go +server/internal/handler/squad_briefing_test.go +server/internal/handler/squad_private_leader_test.go +server/internal/handler/autopilot_private_leader_test.go +server/internal/handler/squad_no_action_test.go +``` + +Verification command: + +```bash +go test ./internal/handler -run 'Test.*Squad|Test.*squad|Test.*Autopilot.*Squad|Test.*ChildDone.*Squad' +``` diff --git a/server/internal/service/builtin_skills_test.go b/server/internal/service/builtin_skills_test.go index 5e649fe4c..1919a45d4 100644 --- a/server/internal/service/builtin_skills_test.go +++ b/server/internal/service/builtin_skills_test.go @@ -354,6 +354,40 @@ func TestCreatingAgentsSkillCoversAgentCreationContracts(t *testing.T) { } } +func TestSquadsSkillCoversLeaderRoutingContract(t *testing.T) { + skill, ok := findSkill(t, "multica-squads") + if !ok { + return + } + fm, body, _ := splitFrontmatter(skill.Content) + + if got := strings.TrimSpace(fm["user-invocable"]); got != "false" { + t.Errorf("user-invocable = %q, want false (squad guidance triggers from context)", got) + } + if got := strings.TrimSpace(fm["allowed-tools"]); !strings.Contains(got, "Bash(multica *)") { + t.Errorf("allowed-tools = %q, want access to the Multica CLI", got) + } + + mustContain := []string{ + "A squad is not an agent", + "squad's `leader_id` agent", + "squad members are not automatically fanned out", + "multica squad member set-role", + "mention://squad/", + "recording squad activity", + "references/squad-source-map.md", + } + for _, want := range mustContain { + if !strings.Contains(body, want) { + t.Errorf("squads skill missing %q", want) + } + } + + if !skillHasFile(skill, "references/squad-source-map.md") { + t.Errorf("squads skill missing supporting file references/squad-source-map.md") + } +} + func findSkill(t *testing.T, name string) (AgentSkillData, bool) { t.Helper() for _, s := range loadBuiltinSkills() { @@ -365,6 +399,15 @@ func findSkill(t *testing.T, name string) (AgentSkillData, bool) { return AgentSkillData{}, false } +func skillHasFile(skill AgentSkillData, path string) bool { + for _, f := range skill.Files { + if f.Path == path { + return true + } + } + return false +} + // splitFrontmatter returns the top-level scalar keys of a leading YAML // frontmatter block, the body after it, and whether a block was found. It only // understands flat `key: value` lines — enough for the template's frontmatter.