mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-02 18:13:27 +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>
153 lines
3.9 KiB
TypeScript
153 lines
3.9 KiB
TypeScript
// AUTO-GENERATED by scripts/generate-reserved-slugs.mjs.
|
|
// Do not edit by hand — edit server/internal/handler/reserved_slugs.json
|
|
// and run `pnpm generate:reserved-slugs`.
|
|
|
|
/**
|
|
* Slugs reserved because they collide with frontend top-level routes,
|
|
* platform features, or web standards.
|
|
*
|
|
* Single source of truth: `server/internal/handler/reserved_slugs.json`.
|
|
* The Go backend embeds that JSON; this file is regenerated from it.
|
|
*
|
|
* Convention for new global routes (CLAUDE.md): use a single word
|
|
* (`/login`, `/inbox`) or `/{noun}/{verb}` (`/workspaces/new`). Hyphenated
|
|
* root-level word groups (`/new-workspace`, `/create-team`) collide with
|
|
* common user workspace names — see PR for full discussion.
|
|
*/
|
|
export const RESERVED_SLUGS: ReadonlySet<string> = new Set([
|
|
// Auth flow
|
|
// `onboarding` is historical, kept reserved post-removal of the route.
|
|
"login",
|
|
"logout",
|
|
"signin",
|
|
"signout",
|
|
"signup",
|
|
"auth",
|
|
"oauth",
|
|
"callback",
|
|
"invite",
|
|
"invitations",
|
|
"verify",
|
|
"reset",
|
|
"password",
|
|
"onboarding",
|
|
|
|
// Platform / marketing routes (current + likely-future)
|
|
// `multica` is reserved as the brand name to block impersonation workspaces.
|
|
// `www`, `new`, `home`, `homepage`, `dashboard` are confusables or
|
|
// likely-future global landing/entry routes; `homepage` matches the existing
|
|
// `/homepage` landing variant in apps/web.
|
|
"api",
|
|
"admin",
|
|
"multica",
|
|
"www",
|
|
"new",
|
|
"home",
|
|
"homepage",
|
|
"dashboard",
|
|
"help",
|
|
"about",
|
|
"pricing",
|
|
"changelog",
|
|
"docs",
|
|
"support",
|
|
"status",
|
|
"legal",
|
|
"privacy",
|
|
"terms",
|
|
"security",
|
|
"contact",
|
|
"blog",
|
|
"careers",
|
|
"press",
|
|
"download",
|
|
|
|
// Account / billing (likely-future global routes in the avatar menu)
|
|
"profile",
|
|
"account",
|
|
"billing",
|
|
"notifications",
|
|
"search",
|
|
"members",
|
|
|
|
// Workspace route segments
|
|
// Reserving each segment name prevents `/{slug}/{view}` from being visually
|
|
// ambiguous (e.g. a workspace named `issues` would make `/issues/abc` mean two
|
|
// things). `workspaces` covers the global `/workspaces/new` workspace-creation
|
|
// page; `teams` is reserved for future team management.
|
|
"issues",
|
|
"projects",
|
|
"autopilots",
|
|
"agents",
|
|
"squads",
|
|
"inbox",
|
|
"my-issues",
|
|
"usage",
|
|
"runtimes",
|
|
"skills",
|
|
"settings",
|
|
"workspaces",
|
|
"teams",
|
|
|
|
// API / integration prefixes
|
|
// `api` above already covers `/api/*`; these guard against future top-level
|
|
// API alias routes (e.g. `/v1`, `/graphql`) and against accidental workspace
|
|
// slugs that read like API identifiers.
|
|
"v1",
|
|
"v2",
|
|
"graphql",
|
|
"webhooks",
|
|
"sdk",
|
|
"tokens",
|
|
"cli",
|
|
|
|
// Backend ops / observability
|
|
// `/health`, `/readyz`, `/healthz`, and `/ws` exist on the backend host;
|
|
// reserving them on the workspace slug space prevents naming confusion if/when
|
|
// these paths are ever proxied through the web origin.
|
|
"health",
|
|
"readyz",
|
|
"healthz",
|
|
"ws",
|
|
"metrics",
|
|
"ping",
|
|
|
|
// RFC 2142 — privileged email mailboxes
|
|
// Allowing user workspaces with these slugs would let attackers spoof system
|
|
// messaging.
|
|
"postmaster",
|
|
"abuse",
|
|
"noreply",
|
|
"webmaster",
|
|
"hostmaster",
|
|
|
|
// Hostname / subdomain confusables
|
|
// Even on path-based routing these names attract phishing and
|
|
// subdomain-takeover attempts.
|
|
"mail",
|
|
"ftp",
|
|
"static",
|
|
"cdn",
|
|
"assets",
|
|
"public",
|
|
"files",
|
|
"uploads",
|
|
|
|
// Next.js / web standards
|
|
// These entries contain characters (dots, underscores) that today's slug regex
|
|
// `^[a-z0-9]+(?:-[a-z0-9]+)*$` already rejects at the format-validation step —
|
|
// so `isReservedSlug` never actually matches them. They are kept as
|
|
// defense-in-depth so that if the slug regex is ever relaxed (e.g. to support
|
|
// dotted corporate slugs like `acme.io`), these system paths stay protected.
|
|
"_next",
|
|
"favicon.ico",
|
|
"robots.txt",
|
|
"sitemap.xml",
|
|
"manifest.json",
|
|
".well-known",
|
|
]);
|
|
|
|
export function isReservedSlug(slug: string): boolean {
|
|
return RESERVED_SLUGS.has(slug);
|
|
}
|