mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 21:39:54 +02:00
* feat: implement Squad feature MVP
- Add migration 084_squad: squad, squad_member, squad_activity_log tables
- Extend issue.assignee_type to support 'squad'
- Add sqlc queries for squad CRUD, member management, activity logs
- Add Go handler with full Squad API (CRUD, members, activity log)
- Register routes: /api/squads/*, /api/issues/{id}/squad-activity, /api/squad-activity
- Add Squad trigger logic:
- Assign Squad immediately triggers leader
- Every external comment on squad-assigned issue triggers leader
- Anti-loop: squad members' comments don't trigger leader
- Dedup: skip if leader already has pending task
- Add squad activity log API (方案 B) for leader no-op recording
- Add frontend TypeScript types (Squad, SquadMember, SquadActivityLog)
- Add protocol events: squad:created, squad:updated, squad:deleted
Co-authored-by: multica-agent <github@multica.ai>
* fix: address PR review blocking issues
1. validateAssigneePair now accepts 'squad' assignee_type
2. All squad endpoints validate workspace ownership via GetSquadInWorkspace
3. CreateSquadActivityLog restricted to squad leader agent only
4. AddSquadMember validates member exists in workspace
5. UpdateSquad auto-adds new leader to squad members
6. DeleteSquad transfers assigned issues to leader before deletion
7. IssueAssigneeType includes 'squad' in frontend types
Co-authored-by: multica-agent <github@multica.ai>
* feat: soft-delete squads via archive instead of hard delete
- Add migration 085: archived_at + archived_by columns on squad table
- ListSquads now excludes archived squads (ListAllSquads for admin)
- DeleteSquad → ArchiveSquad (sets archived_at, preserves all records)
- Transfer squad-assigned issues to leader before archiving
- SquadResponse includes archived_at/archived_by fields
- Frontend Squad type updated with nullable archived fields
Co-authored-by: multica-agent <github@multica.ai>
* feat: re-add Squads frontend entry (sidebar nav + pages)
Re-applies the frontend squad entry that was lost during a merge:
- Sidebar nav: Squads item with Users icon
- Paths: squads() and squadDetail() in workspace paths
- Routes: /squads and /squads/[id] pages
- Views: SquadsPage (list) and SquadDetailPage
- i18n: en 'Squads' / zh '小队'
- Reserved slug: 'squads'
Co-authored-by: multica-agent <github@multica.ai>
* fix: fix SquadsPage rendering - use PageHeader children pattern
PageHeader takes children, not title/actions props. The incorrect
usage caused a React rendering error. Now matches the pattern used
by autopilots and agents pages.
Co-authored-by: multica-agent <github@multica.ai>
* fix(squads): add API client methods and package export for squads pages
* feat: complete Squad frontend - create dialog, member management, API methods
- Add CreateSquadModal with name/description/leader selection
- Register 'create-squad' in modal registry
- Wire 'New Squad' button to open the modal
- Add full API client methods: createSquad, updateSquad, deleteSquad,
addSquadMember, removeSquadMember
- Rewrite SquadDetailPage with:
- Member list showing resolved names
- Add/remove member UI
- Archive squad button
- Back navigation to squads list
Co-authored-by: multica-agent <github@multica.ai>
* feat: improve Squad UI - match create agent dialog style
- CreateSquadModal: proper Dialog with Header/Description/Footer,
agent picker with avatars, textarea for description
- SquadDetailPage: centered max-w-2xl layout, ActorAvatar for members,
Crown badge for leader, textarea for member description,
improved spacing and visual hierarchy
- Renamed 'role' field label to 'Description' in add member form
(describes the member's responsibilities in the squad)
Co-authored-by: multica-agent <github@multica.ai>
* feat(squad): add avatar, instructions; drop unique-name constraint
- 086: add squad.avatar_url
- 087: drop unique constraint on squad.name (squads with the same
name are legitimate across teams; uniqueness was an accidental
product constraint)
- 088: add squad.instructions (text, default '')
- UpdateSquad now COALESCEs avatar_url + instructions
- handler exposes Instructions in SquadResponse and accepts it in
UpdateSquad
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(squad): assignable + mention target; trigger leader on assign
- assignee picker and @mention suggestion list squads alongside
agents and members; renders squad avatar/icon
- creating or updating an issue with assignee_type=squad enqueues
a task for the squad's current leader (mirrors agent-assignee
parking-lot rule: skip backlog only)
- workspace queries/hooks expose squads where needed for the
pickers
- locales updated for new picker copy
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(squad): agent-style detail page with members + instructions tabs
- restructure squad detail page to mirror the agent detail page:
320px inspector (creator, leader, created/updated) + tabbed
pane (Members | Instructions) with dirty-guard AlertDialog
- inline name + avatar editing on the inspector
- inline description editor (modal textarea)
- members tab: leader + member picker with role descriptions,
swap leader, edit member roles, remove
- instructions tab: ContentEditor + Save (mirrors agent pattern)
- squads list shows the squad avatar/icon
- core types + api.updateSquad accept avatar_url + instructions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(squad): inject leader briefing on claim (protocol + roster + instructions)
When a squad's leader agent claims a task on a squad-assigned issue,
append a system-level briefing to the agent's Instructions composed of:
1. Squad Operating Protocol — hard-coded rules: leader is a
coordinator, dispatch via @mention, stop after dispatching,
resume on re-trigger, do not work outside the roster.
2. Squad Roster — leader self-row plus one row per non-archived
member with a literal mention markdown string ([@Name](mention://
agent|member/<UUID>)) the leader can paste verbatim. Round-trips
through util.ParseMentions, enforced by a contract test.
3. Squad Instructions — the user-defined squad.instructions block,
omitted entirely when empty so we do not leave a dangling heading.
Non-leader members claiming the same issue receive no briefing.
Tests cover: full squad with mixed agent/human members, lone leader,
archived agents skipped, empty user instructions, mention round-trip,
and the leader/non-leader claim-handler gate.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(squad): tell leader not to restate issue context in dispatch comment
After observing leaders padding their delegation comments with full
re-summaries of the issue body and prior discussion, make the
Operating Protocol explicit:
- assignees on Multica already have the full issue (title,
description, all comments, attachments) and workspace context;
- delegation comments should add only what cannot be inferred
(who is picked, why, extra constraints), aim for two or three
sentences;
- restating context is now an explicit hard rule violation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(squad): unify leader evaluation into activity_log, add CLI command
- Squad member comments now trigger leader (only leader self-excluded)
- Replace squad_activity_log with activity_log (action: squad_leader_evaluated)
- Add CLI: multica squad activity <issue-id> <outcome> --reason
- Add API: POST /api/issues/{id}/squad-evaluated
- Update squad operating protocol to require evaluation recording
- Remove squad_activity_log table from schema and generated code
* feat(cli): add squad list, get, member list commands
* fix(squad): address review findings (P1+P2)
P1 fixes:
- Add 'squads' to reserved_slugs.json (source of truth)
- Add 'create-squad' to ModalType union
- Remove unused leaderOpen/selectedLeader in create-squad modal
- Replace literal JSX strings with i18n selectors (en + zh-Hans)
P2 fixes:
- Add 'squad' to mention regex (MentionRe)
- Fix human member lookup in squad briefing (use GetUser directly)
- Add squads routes to desktop app
- Add squad:created/updated/deleted to WSEventType + invalidation
- Reject archived squads as issue assignees
* fix(squad): restore zh-Hans key, publish activity event, invalidate issues on archive
- Restore create_project.title in zh-Hans modals.json (dropped by prior edit)
- Publish activity:created WS event after squad leader evaluation
- Invalidate issue queries on squad:deleted (archive transfers assignees)
- Add creator info to squad list cards
* fix(squad): realtime sync, rerun support, leader validation
- Use workspaceKeys.squads prefix for detail/member queries (realtime invalidation)
- Publish squad:updated after add/remove/role-change member mutations
- Support rerun for squad-assigned issues (targets leader agent)
- Reject assignment to squads whose leader is archived
---------
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
494 lines
13 KiB
Go
494 lines
13 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.30.0
|
|
// source: squad.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const addSquadMember = `-- name: AddSquadMember :one
|
|
INSERT INTO squad_member (squad_id, member_type, member_id, role)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING id, squad_id, member_type, member_id, role, created_at
|
|
`
|
|
|
|
type AddSquadMemberParams struct {
|
|
SquadID pgtype.UUID `json:"squad_id"`
|
|
MemberType string `json:"member_type"`
|
|
MemberID pgtype.UUID `json:"member_id"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
func (q *Queries) AddSquadMember(ctx context.Context, arg AddSquadMemberParams) (SquadMember, error) {
|
|
row := q.db.QueryRow(ctx, addSquadMember,
|
|
arg.SquadID,
|
|
arg.MemberType,
|
|
arg.MemberID,
|
|
arg.Role,
|
|
)
|
|
var i SquadMember
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.SquadID,
|
|
&i.MemberType,
|
|
&i.MemberID,
|
|
&i.Role,
|
|
&i.CreatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const archiveSquad = `-- name: ArchiveSquad :one
|
|
UPDATE squad SET archived_at = now(), archived_by = $2, updated_at = now()
|
|
WHERE id = $1
|
|
RETURNING id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions
|
|
`
|
|
|
|
type ArchiveSquadParams struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
ArchivedBy pgtype.UUID `json:"archived_by"`
|
|
}
|
|
|
|
func (q *Queries) ArchiveSquad(ctx context.Context, arg ArchiveSquadParams) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, archiveSquad, arg.ID, arg.ArchivedBy)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const countSquadMembers = `-- name: CountSquadMembers :one
|
|
SELECT count(*) FROM squad_member WHERE squad_id = $1
|
|
`
|
|
|
|
func (q *Queries) CountSquadMembers(ctx context.Context, squadID pgtype.UUID) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countSquadMembers, squadID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const createSquad = `-- name: CreateSquad :one
|
|
INSERT INTO squad (workspace_id, name, description, leader_id, creator_id)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions
|
|
`
|
|
|
|
type CreateSquadParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
LeaderID pgtype.UUID `json:"leader_id"`
|
|
CreatorID pgtype.UUID `json:"creator_id"`
|
|
}
|
|
|
|
func (q *Queries) CreateSquad(ctx context.Context, arg CreateSquadParams) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, createSquad,
|
|
arg.WorkspaceID,
|
|
arg.Name,
|
|
arg.Description,
|
|
arg.LeaderID,
|
|
arg.CreatorID,
|
|
)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getSquad = `-- name: GetSquad :one
|
|
SELECT id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions FROM squad WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetSquad(ctx context.Context, id pgtype.UUID) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, getSquad, id)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getSquadByAssignee = `-- name: GetSquadByAssignee :one
|
|
SELECT s.id, s.workspace_id, s.name, s.description, s.leader_id, s.creator_id, s.created_at, s.updated_at, s.archived_at, s.archived_by, s.avatar_url, s.instructions FROM squad s WHERE s.id = $1 AND s.workspace_id = $2
|
|
`
|
|
|
|
type GetSquadByAssigneeParams struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
}
|
|
|
|
// Look up the squad when an issue is assigned to a squad.
|
|
func (q *Queries) GetSquadByAssignee(ctx context.Context, arg GetSquadByAssigneeParams) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, getSquadByAssignee, arg.ID, arg.WorkspaceID)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getSquadInWorkspace = `-- name: GetSquadInWorkspace :one
|
|
SELECT id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions FROM squad WHERE id = $1 AND workspace_id = $2
|
|
`
|
|
|
|
type GetSquadInWorkspaceParams struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
}
|
|
|
|
func (q *Queries) GetSquadInWorkspace(ctx context.Context, arg GetSquadInWorkspaceParams) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, getSquadInWorkspace, arg.ID, arg.WorkspaceID)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const isSquadMember = `-- name: IsSquadMember :one
|
|
SELECT EXISTS(
|
|
SELECT 1 FROM squad_member
|
|
WHERE squad_id = $1 AND member_type = $2 AND member_id = $3
|
|
) AS is_member
|
|
`
|
|
|
|
type IsSquadMemberParams struct {
|
|
SquadID pgtype.UUID `json:"squad_id"`
|
|
MemberType string `json:"member_type"`
|
|
MemberID pgtype.UUID `json:"member_id"`
|
|
}
|
|
|
|
func (q *Queries) IsSquadMember(ctx context.Context, arg IsSquadMemberParams) (bool, error) {
|
|
row := q.db.QueryRow(ctx, isSquadMember, arg.SquadID, arg.MemberType, arg.MemberID)
|
|
var is_member bool
|
|
err := row.Scan(&is_member)
|
|
return is_member, err
|
|
}
|
|
|
|
const listAllSquads = `-- name: ListAllSquads :many
|
|
SELECT id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions FROM squad WHERE workspace_id = $1 ORDER BY created_at ASC
|
|
`
|
|
|
|
func (q *Queries) ListAllSquads(ctx context.Context, workspaceID pgtype.UUID) ([]Squad, error) {
|
|
rows, err := q.db.Query(ctx, listAllSquads, workspaceID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Squad{}
|
|
for rows.Next() {
|
|
var i Squad
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listSquadMembers = `-- name: ListSquadMembers :many
|
|
SELECT id, squad_id, member_type, member_id, role, created_at FROM squad_member WHERE squad_id = $1 ORDER BY created_at ASC
|
|
`
|
|
|
|
func (q *Queries) ListSquadMembers(ctx context.Context, squadID pgtype.UUID) ([]SquadMember, error) {
|
|
rows, err := q.db.Query(ctx, listSquadMembers, squadID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []SquadMember{}
|
|
for rows.Next() {
|
|
var i SquadMember
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.SquadID,
|
|
&i.MemberType,
|
|
&i.MemberID,
|
|
&i.Role,
|
|
&i.CreatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listSquads = `-- name: ListSquads :many
|
|
SELECT id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions FROM squad WHERE workspace_id = $1 AND archived_at IS NULL ORDER BY created_at ASC
|
|
`
|
|
|
|
func (q *Queries) ListSquads(ctx context.Context, workspaceID pgtype.UUID) ([]Squad, error) {
|
|
rows, err := q.db.Query(ctx, listSquads, workspaceID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Squad{}
|
|
for rows.Next() {
|
|
var i Squad
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listSquadsByMember = `-- name: ListSquadsByMember :many
|
|
SELECT s.id, s.workspace_id, s.name, s.description, s.leader_id, s.creator_id, s.created_at, s.updated_at, s.archived_at, s.archived_by, s.avatar_url, s.instructions FROM squad s
|
|
JOIN squad_member sm ON sm.squad_id = s.id
|
|
WHERE s.workspace_id = $1 AND sm.member_type = $2 AND sm.member_id = $3
|
|
ORDER BY s.created_at ASC
|
|
`
|
|
|
|
type ListSquadsByMemberParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
MemberType string `json:"member_type"`
|
|
MemberID pgtype.UUID `json:"member_id"`
|
|
}
|
|
|
|
// Find all squads a given entity belongs to in a workspace.
|
|
func (q *Queries) ListSquadsByMember(ctx context.Context, arg ListSquadsByMemberParams) ([]Squad, error) {
|
|
rows, err := q.db.Query(ctx, listSquadsByMember, arg.WorkspaceID, arg.MemberType, arg.MemberID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Squad{}
|
|
for rows.Next() {
|
|
var i Squad
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const removeSquadMember = `-- name: RemoveSquadMember :exec
|
|
DELETE FROM squad_member
|
|
WHERE squad_id = $1 AND member_type = $2 AND member_id = $3
|
|
`
|
|
|
|
type RemoveSquadMemberParams struct {
|
|
SquadID pgtype.UUID `json:"squad_id"`
|
|
MemberType string `json:"member_type"`
|
|
MemberID pgtype.UUID `json:"member_id"`
|
|
}
|
|
|
|
func (q *Queries) RemoveSquadMember(ctx context.Context, arg RemoveSquadMemberParams) error {
|
|
_, err := q.db.Exec(ctx, removeSquadMember, arg.SquadID, arg.MemberType, arg.MemberID)
|
|
return err
|
|
}
|
|
|
|
const transferSquadAssignees = `-- name: TransferSquadAssignees :exec
|
|
UPDATE issue SET assignee_type = 'agent', assignee_id = $2, updated_at = now()
|
|
WHERE assignee_type = 'squad' AND assignee_id = $1
|
|
`
|
|
|
|
type TransferSquadAssigneesParams struct {
|
|
AssigneeID pgtype.UUID `json:"assignee_id"`
|
|
AssigneeID_2 pgtype.UUID `json:"assignee_id_2"`
|
|
}
|
|
|
|
// Transfer all issues assigned to a squad to the squad's leader agent.
|
|
func (q *Queries) TransferSquadAssignees(ctx context.Context, arg TransferSquadAssigneesParams) error {
|
|
_, err := q.db.Exec(ctx, transferSquadAssignees, arg.AssigneeID, arg.AssigneeID_2)
|
|
return err
|
|
}
|
|
|
|
const updateSquad = `-- name: UpdateSquad :one
|
|
UPDATE squad SET
|
|
name = COALESCE($2, name),
|
|
description = COALESCE($3, description),
|
|
leader_id = COALESCE($4, leader_id),
|
|
avatar_url = COALESCE($5, avatar_url),
|
|
instructions = COALESCE($6, instructions),
|
|
updated_at = now()
|
|
WHERE id = $1
|
|
RETURNING id, workspace_id, name, description, leader_id, creator_id, created_at, updated_at, archived_at, archived_by, avatar_url, instructions
|
|
`
|
|
|
|
type UpdateSquadParams struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name pgtype.Text `json:"name"`
|
|
Description pgtype.Text `json:"description"`
|
|
LeaderID pgtype.UUID `json:"leader_id"`
|
|
AvatarUrl pgtype.Text `json:"avatar_url"`
|
|
Instructions pgtype.Text `json:"instructions"`
|
|
}
|
|
|
|
func (q *Queries) UpdateSquad(ctx context.Context, arg UpdateSquadParams) (Squad, error) {
|
|
row := q.db.QueryRow(ctx, updateSquad,
|
|
arg.ID,
|
|
arg.Name,
|
|
arg.Description,
|
|
arg.LeaderID,
|
|
arg.AvatarUrl,
|
|
arg.Instructions,
|
|
)
|
|
var i Squad
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.WorkspaceID,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.LeaderID,
|
|
&i.CreatorID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArchivedAt,
|
|
&i.ArchivedBy,
|
|
&i.AvatarUrl,
|
|
&i.Instructions,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateSquadMemberRole = `-- name: UpdateSquadMemberRole :one
|
|
UPDATE squad_member SET role = $4
|
|
WHERE squad_id = $1 AND member_type = $2 AND member_id = $3
|
|
RETURNING id, squad_id, member_type, member_id, role, created_at
|
|
`
|
|
|
|
type UpdateSquadMemberRoleParams struct {
|
|
SquadID pgtype.UUID `json:"squad_id"`
|
|
MemberType string `json:"member_type"`
|
|
MemberID pgtype.UUID `json:"member_id"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
func (q *Queries) UpdateSquadMemberRole(ctx context.Context, arg UpdateSquadMemberRoleParams) (SquadMember, error) {
|
|
row := q.db.QueryRow(ctx, updateSquadMemberRole,
|
|
arg.SquadID,
|
|
arg.MemberType,
|
|
arg.MemberID,
|
|
arg.Role,
|
|
)
|
|
var i SquadMember
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.SquadID,
|
|
&i.MemberType,
|
|
&i.MemberID,
|
|
&i.Role,
|
|
&i.CreatedAt,
|
|
)
|
|
return i, err
|
|
}
|