forked from cardosofelipe/fast-next-template
feat(agents): add category and display fields to AgentType model
Add 6 new fields to AgentType for better organization and UI display: - category: enum for grouping (development, design, quality, etc.) - icon: Lucide icon identifier for UI - color: hex color code for visual distinction - sort_order: display ordering within categories - typical_tasks: list of tasks the agent excels at - collaboration_hints: agent slugs that work well together Backend changes: - Add AgentTypeCategory enum to enums.py - Update AgentType model with 6 new columns and indexes - Update schemas with validators for new fields - Add category filter and /grouped endpoint to routes - Update CRUD with get_grouped_by_category method - Update seed data with categories for all 27 agents - Add migration 0007 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Add category and display fields to agent_types table
|
||||
|
||||
Revision ID: 0007
|
||||
Revises: 0006
|
||||
Create Date: 2026-01-06
|
||||
|
||||
This migration adds:
|
||||
- category: String(50) for grouping agents by role type
|
||||
- icon: String(50) for Lucide icon identifier
|
||||
- color: String(7) for hex color code
|
||||
- sort_order: Integer for display ordering within categories
|
||||
- typical_tasks: JSONB list of tasks this agent excels at
|
||||
- collaboration_hints: JSONB list of agent slugs that work well together
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "0007"
|
||||
down_revision: str | None = "0006"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add category and display fields to agent_types table."""
|
||||
# Add new columns
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column("category", sa.String(length=50), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column("icon", sa.String(length=50), nullable=True, server_default="bot"),
|
||||
)
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column(
|
||||
"color", sa.String(length=7), nullable=True, server_default="#3B82F6"
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"),
|
||||
)
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column(
|
||||
"typical_tasks",
|
||||
postgresql.JSONB(astext_type=sa.Text()),
|
||||
nullable=False,
|
||||
server_default="[]",
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"agent_types",
|
||||
sa.Column(
|
||||
"collaboration_hints",
|
||||
postgresql.JSONB(astext_type=sa.Text()),
|
||||
nullable=False,
|
||||
server_default="[]",
|
||||
),
|
||||
)
|
||||
|
||||
# Add indexes for category and sort_order
|
||||
op.create_index("ix_agent_types_category", "agent_types", ["category"])
|
||||
op.create_index("ix_agent_types_sort_order", "agent_types", ["sort_order"])
|
||||
op.create_index(
|
||||
"ix_agent_types_category_sort", "agent_types", ["category", "sort_order"]
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove category and display fields from agent_types table."""
|
||||
# Drop indexes
|
||||
op.drop_index("ix_agent_types_category_sort", table_name="agent_types")
|
||||
op.drop_index("ix_agent_types_sort_order", table_name="agent_types")
|
||||
op.drop_index("ix_agent_types_category", table_name="agent_types")
|
||||
|
||||
# Drop columns
|
||||
op.drop_column("agent_types", "collaboration_hints")
|
||||
op.drop_column("agent_types", "typical_tasks")
|
||||
op.drop_column("agent_types", "sort_order")
|
||||
op.drop_column("agent_types", "color")
|
||||
op.drop_column("agent_types", "icon")
|
||||
op.drop_column("agent_types", "category")
|
||||
@@ -81,6 +81,13 @@ def _build_agent_type_response(
|
||||
mcp_servers=agent_type.mcp_servers,
|
||||
tool_permissions=agent_type.tool_permissions,
|
||||
is_active=agent_type.is_active,
|
||||
# Category and display fields
|
||||
category=agent_type.category,
|
||||
icon=agent_type.icon,
|
||||
color=agent_type.color,
|
||||
sort_order=agent_type.sort_order,
|
||||
typical_tasks=agent_type.typical_tasks or [],
|
||||
collaboration_hints=agent_type.collaboration_hints or [],
|
||||
created_at=agent_type.created_at,
|
||||
updated_at=agent_type.updated_at,
|
||||
instance_count=instance_count,
|
||||
@@ -300,6 +307,7 @@ async def list_agent_types(
|
||||
request: Request,
|
||||
pagination: PaginationParams = Depends(),
|
||||
is_active: bool = Query(True, description="Filter by active status"),
|
||||
category: str | None = Query(None, description="Filter by category"),
|
||||
search: str | None = Query(None, description="Search by name, slug, description"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
@@ -314,6 +322,7 @@ async def list_agent_types(
|
||||
request: FastAPI request object
|
||||
pagination: Pagination parameters (page, limit)
|
||||
is_active: Filter by active status (default: True)
|
||||
category: Filter by category (e.g., "development", "design")
|
||||
search: Optional search term for name, slug, description
|
||||
current_user: Authenticated user
|
||||
db: Database session
|
||||
@@ -328,6 +337,7 @@ async def list_agent_types(
|
||||
skip=pagination.offset,
|
||||
limit=pagination.limit,
|
||||
is_active=is_active,
|
||||
category=category,
|
||||
search=search,
|
||||
)
|
||||
|
||||
@@ -354,6 +364,51 @@ async def list_agent_types(
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/grouped",
|
||||
response_model=dict[str, list[AgentTypeResponse]],
|
||||
summary="List Agent Types Grouped by Category",
|
||||
description="Get all agent types organized by category",
|
||||
operation_id="list_agent_types_grouped",
|
||||
)
|
||||
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
|
||||
async def list_agent_types_grouped(
|
||||
request: Request,
|
||||
is_active: bool = Query(True, description="Filter by active status"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Get agent types grouped by category.
|
||||
|
||||
Returns a dictionary where keys are category names and values
|
||||
are lists of agent types, sorted by sort_order within each category.
|
||||
|
||||
Args:
|
||||
request: FastAPI request object
|
||||
is_active: Filter by active status (default: True)
|
||||
current_user: Authenticated user
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Dictionary mapping category to list of agent types
|
||||
"""
|
||||
try:
|
||||
grouped = await agent_type_crud.get_grouped_by_category(db, is_active=is_active)
|
||||
|
||||
# Transform to response objects
|
||||
result: dict[str, list[AgentTypeResponse]] = {}
|
||||
for category, types in grouped.items():
|
||||
result[category] = [
|
||||
_build_agent_type_response(t, instance_count=0) for t in types
|
||||
]
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting grouped agent types: {e!s}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{agent_type_id}",
|
||||
response_model=AgentTypeResponse,
|
||||
|
||||
@@ -43,6 +43,13 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
mcp_servers=obj_in.mcp_servers,
|
||||
tool_permissions=obj_in.tool_permissions,
|
||||
is_active=obj_in.is_active,
|
||||
# Category and display fields
|
||||
category=obj_in.category.value if obj_in.category else None,
|
||||
icon=obj_in.icon,
|
||||
color=obj_in.color,
|
||||
sort_order=obj_in.sort_order,
|
||||
typical_tasks=obj_in.typical_tasks,
|
||||
collaboration_hints=obj_in.collaboration_hints,
|
||||
)
|
||||
db.add(db_obj)
|
||||
await db.commit()
|
||||
@@ -68,6 +75,7 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
is_active: bool | None = None,
|
||||
category: str | None = None,
|
||||
search: str | None = None,
|
||||
sort_by: str = "created_at",
|
||||
sort_order: str = "desc",
|
||||
@@ -85,6 +93,9 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
if is_active is not None:
|
||||
query = query.where(AgentType.is_active == is_active)
|
||||
|
||||
if category:
|
||||
query = query.where(AgentType.category == category)
|
||||
|
||||
if search:
|
||||
search_filter = or_(
|
||||
AgentType.name.ilike(f"%{search}%"),
|
||||
@@ -162,6 +173,7 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
is_active: bool | None = None,
|
||||
category: str | None = None,
|
||||
search: str | None = None,
|
||||
) -> tuple[list[dict[str, Any]], int]:
|
||||
"""
|
||||
@@ -177,6 +189,7 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
is_active=is_active,
|
||||
category=category,
|
||||
search=search,
|
||||
)
|
||||
|
||||
@@ -260,6 +273,44 @@ class CRUDAgentType(CRUDBase[AgentType, AgentTypeCreate, AgentTypeUpdate]):
|
||||
)
|
||||
raise
|
||||
|
||||
async def get_grouped_by_category(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
*,
|
||||
is_active: bool = True,
|
||||
) -> dict[str, list[AgentType]]:
|
||||
"""
|
||||
Get agent types grouped by category, sorted by sort_order within each group.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
is_active: Filter by active status (default: True)
|
||||
|
||||
Returns:
|
||||
Dictionary mapping category to list of agent types
|
||||
"""
|
||||
try:
|
||||
query = (
|
||||
select(AgentType)
|
||||
.where(AgentType.is_active == is_active)
|
||||
.order_by(AgentType.category, AgentType.sort_order, AgentType.name)
|
||||
)
|
||||
result = await db.execute(query)
|
||||
agent_types = list(result.scalars().all())
|
||||
|
||||
# Group by category
|
||||
grouped: dict[str, list[AgentType]] = {}
|
||||
for at in agent_types:
|
||||
cat: str = str(at.category) if at.category else "uncategorized"
|
||||
if cat not in grouped:
|
||||
grouped[cat] = []
|
||||
grouped[cat].append(at)
|
||||
|
||||
return grouped
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting grouped agent types: {e!s}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
# Create a singleton instance for use across the application
|
||||
agent_type = CRUDAgentType(AgentType)
|
||||
|
||||
@@ -149,6 +149,13 @@ async def load_default_agent_types(session: AsyncSession) -> None:
|
||||
mcp_servers=agent_type_data.get("mcp_servers", []),
|
||||
tool_permissions=agent_type_data.get("tool_permissions", {}),
|
||||
is_active=agent_type_data.get("is_active", True),
|
||||
# Category and display fields
|
||||
category=agent_type_data.get("category"),
|
||||
icon=agent_type_data.get("icon", "bot"),
|
||||
color=agent_type_data.get("color", "#3B82F6"),
|
||||
sort_order=agent_type_data.get("sort_order", 0),
|
||||
typical_tasks=agent_type_data.get("typical_tasks", []),
|
||||
collaboration_hints=agent_type_data.get("collaboration_hints", []),
|
||||
)
|
||||
|
||||
await agent_type_crud.create(session, obj_in=agent_type_in)
|
||||
|
||||
@@ -6,7 +6,7 @@ An AgentType is a template that defines the capabilities, personality,
|
||||
and model configuration for agent instances.
|
||||
"""
|
||||
|
||||
from sqlalchemy import Boolean, Column, Index, String, Text
|
||||
from sqlalchemy import Boolean, Column, Index, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
@@ -56,6 +56,24 @@ class AgentType(Base, UUIDMixin, TimestampMixin):
|
||||
# Whether this agent type is available for new instances
|
||||
is_active = Column(Boolean, default=True, nullable=False, index=True)
|
||||
|
||||
# Category for grouping agents (development, design, quality, etc.)
|
||||
category = Column(String(50), nullable=True, index=True)
|
||||
|
||||
# Lucide icon identifier for UI display (e.g., "code", "palette", "shield")
|
||||
icon = Column(String(50), nullable=True, default="bot")
|
||||
|
||||
# Hex color code for visual distinction (e.g., "#3B82F6")
|
||||
color = Column(String(7), nullable=True, default="#3B82F6")
|
||||
|
||||
# Display ordering within category (lower = first)
|
||||
sort_order = Column(Integer, nullable=False, default=0, index=True)
|
||||
|
||||
# List of typical tasks this agent excels at
|
||||
typical_tasks = Column(JSONB, default=list, nullable=False)
|
||||
|
||||
# List of agent slugs that collaborate well with this type
|
||||
collaboration_hints = Column(JSONB, default=list, nullable=False)
|
||||
|
||||
# Relationships
|
||||
instances = relationship(
|
||||
"AgentInstance",
|
||||
@@ -66,6 +84,7 @@ class AgentType(Base, UUIDMixin, TimestampMixin):
|
||||
__table_args__ = (
|
||||
Index("ix_agent_types_slug_active", "slug", "is_active"),
|
||||
Index("ix_agent_types_name_active", "name", "is_active"),
|
||||
Index("ix_agent_types_category_sort", "category", "sort_order"),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
||||
@@ -167,3 +167,29 @@ class SprintStatus(str, PyEnum):
|
||||
IN_REVIEW = "in_review"
|
||||
COMPLETED = "completed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class AgentTypeCategory(str, PyEnum):
|
||||
"""
|
||||
Category classification for agent types.
|
||||
|
||||
Used for grouping and filtering agents in the UI.
|
||||
|
||||
DEVELOPMENT: Product, project, and engineering roles
|
||||
DESIGN: UI/UX and design research roles
|
||||
QUALITY: QA and security engineering
|
||||
OPERATIONS: DevOps and MLOps
|
||||
AI_ML: Machine learning and AI specialists
|
||||
DATA: Data science and engineering
|
||||
LEADERSHIP: Technical leadership roles
|
||||
DOMAIN_EXPERT: Industry and domain specialists
|
||||
"""
|
||||
|
||||
DEVELOPMENT = "development"
|
||||
DESIGN = "design"
|
||||
QUALITY = "quality"
|
||||
OPERATIONS = "operations"
|
||||
AI_ML = "ai_ml"
|
||||
DATA = "data"
|
||||
LEADERSHIP = "leadership"
|
||||
DOMAIN_EXPERT = "domain_expert"
|
||||
|
||||
@@ -10,6 +10,8 @@ from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from app.models.syndarix.enums import AgentTypeCategory
|
||||
|
||||
|
||||
class AgentTypeBase(BaseModel):
|
||||
"""Base agent type schema with common fields."""
|
||||
@@ -26,6 +28,14 @@ class AgentTypeBase(BaseModel):
|
||||
tool_permissions: dict[str, Any] = Field(default_factory=dict)
|
||||
is_active: bool = True
|
||||
|
||||
# Category and display fields
|
||||
category: AgentTypeCategory | None = None
|
||||
icon: str | None = Field(None, max_length=50)
|
||||
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
|
||||
sort_order: int = Field(default=0, ge=0, le=1000)
|
||||
typical_tasks: list[str] = Field(default_factory=list)
|
||||
collaboration_hints: list[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("slug")
|
||||
@classmethod
|
||||
def validate_slug(cls, v: str | None) -> str | None:
|
||||
@@ -62,6 +72,18 @@ class AgentTypeBase(BaseModel):
|
||||
"""Validate MCP server list."""
|
||||
return [s.strip() for s in v if s.strip()]
|
||||
|
||||
@field_validator("typical_tasks")
|
||||
@classmethod
|
||||
def validate_typical_tasks(cls, v: list[str]) -> list[str]:
|
||||
"""Validate and normalize typical tasks list."""
|
||||
return [t.strip() for t in v if t.strip()]
|
||||
|
||||
@field_validator("collaboration_hints")
|
||||
@classmethod
|
||||
def validate_collaboration_hints(cls, v: list[str]) -> list[str]:
|
||||
"""Validate and normalize collaboration hints (agent slugs)."""
|
||||
return [h.strip().lower() for h in v if h.strip()]
|
||||
|
||||
|
||||
class AgentTypeCreate(AgentTypeBase):
|
||||
"""Schema for creating a new agent type."""
|
||||
@@ -87,6 +109,14 @@ class AgentTypeUpdate(BaseModel):
|
||||
tool_permissions: dict[str, Any] | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
# Category and display fields (all optional for updates)
|
||||
category: AgentTypeCategory | None = None
|
||||
icon: str | None = Field(None, max_length=50)
|
||||
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
|
||||
sort_order: int | None = Field(None, ge=0, le=1000)
|
||||
typical_tasks: list[str] | None = None
|
||||
collaboration_hints: list[str] | None = None
|
||||
|
||||
@field_validator("slug")
|
||||
@classmethod
|
||||
def validate_slug(cls, v: str | None) -> str | None:
|
||||
@@ -119,6 +149,22 @@ class AgentTypeUpdate(BaseModel):
|
||||
return v
|
||||
return [e.strip().lower() for e in v if e.strip()]
|
||||
|
||||
@field_validator("typical_tasks")
|
||||
@classmethod
|
||||
def validate_typical_tasks(cls, v: list[str] | None) -> list[str] | None:
|
||||
"""Validate and normalize typical tasks list."""
|
||||
if v is None:
|
||||
return v
|
||||
return [t.strip() for t in v if t.strip()]
|
||||
|
||||
@field_validator("collaboration_hints")
|
||||
@classmethod
|
||||
def validate_collaboration_hints(cls, v: list[str] | None) -> list[str] | None:
|
||||
"""Validate and normalize collaboration hints (agent slugs)."""
|
||||
if v is None:
|
||||
return v
|
||||
return [h.strip().lower() for h in v if h.strip()]
|
||||
|
||||
|
||||
class AgentTypeInDB(AgentTypeBase):
|
||||
"""Schema for agent type in database."""
|
||||
|
||||
@@ -29,7 +29,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "clipboard-check",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Requirements discovery", "User story creation", "Backlog prioritization", "Stakeholder alignment"],
|
||||
"collaboration_hints": ["business-analyst", "solutions-architect", "scrum-master"]
|
||||
},
|
||||
{
|
||||
"name": "Project Manager",
|
||||
@@ -61,7 +67,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "briefcase",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Sprint planning", "Risk management", "Status reporting", "Team coordination"],
|
||||
"collaboration_hints": ["product-owner", "scrum-master", "technical-lead"]
|
||||
},
|
||||
{
|
||||
"name": "Business Analyst",
|
||||
@@ -93,7 +105,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "file-text",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 30,
|
||||
"typical_tasks": ["Requirements analysis", "Process modeling", "Gap analysis", "Functional specifications"],
|
||||
"collaboration_hints": ["product-owner", "solutions-architect", "qa-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Solutions Architect",
|
||||
@@ -129,7 +147,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "git-branch",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 40,
|
||||
"typical_tasks": ["System design", "ADR creation", "Technology selection", "Integration patterns"],
|
||||
"collaboration_hints": ["backend-engineer", "frontend-engineer", "security-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Full Stack Engineer",
|
||||
@@ -166,7 +190,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request", "gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "code",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 50,
|
||||
"typical_tasks": ["End-to-end feature development", "API design", "UI implementation", "Database operations"],
|
||||
"collaboration_hints": ["solutions-architect", "qa-engineer", "devops-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Backend Engineer",
|
||||
@@ -208,7 +238,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request", "gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "server",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 60,
|
||||
"typical_tasks": ["API development", "Database optimization", "System integration", "Performance tuning"],
|
||||
"collaboration_hints": ["solutions-architect", "frontend-engineer", "data-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Frontend Engineer",
|
||||
@@ -249,7 +285,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request", "gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "layout",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 70,
|
||||
"typical_tasks": ["UI component development", "State management", "API integration", "Responsive design"],
|
||||
"collaboration_hints": ["ui-ux-designer", "backend-engineer", "qa-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Mobile Engineer",
|
||||
@@ -286,7 +328,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request", "gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "development",
|
||||
"icon": "smartphone",
|
||||
"color": "#3B82F6",
|
||||
"sort_order": 80,
|
||||
"typical_tasks": ["Native app development", "Cross-platform solutions", "Mobile optimization", "App store deployment"],
|
||||
"collaboration_hints": ["backend-engineer", "ui-ux-designer", "qa-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "UI/UX Designer",
|
||||
@@ -321,7 +369,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "design",
|
||||
"icon": "palette",
|
||||
"color": "#EC4899",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Interface design", "User flow creation", "Design system maintenance", "Prototyping"],
|
||||
"collaboration_hints": ["frontend-engineer", "ux-researcher", "product-owner"]
|
||||
},
|
||||
{
|
||||
"name": "UX Researcher",
|
||||
@@ -355,7 +409,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "design",
|
||||
"icon": "search",
|
||||
"color": "#EC4899",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["User research", "Usability testing", "Journey mapping", "Research synthesis"],
|
||||
"collaboration_hints": ["ui-ux-designer", "product-owner", "business-analyst"]
|
||||
},
|
||||
{
|
||||
"name": "QA Engineer",
|
||||
@@ -391,7 +451,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "quality",
|
||||
"icon": "shield",
|
||||
"color": "#10B981",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Test strategy development", "Test automation", "Bug verification", "Quality metrics"],
|
||||
"collaboration_hints": ["backend-engineer", "frontend-engineer", "devops-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "DevOps Engineer",
|
||||
@@ -431,7 +497,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_release", "gitea:delete_*"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "operations",
|
||||
"icon": "settings",
|
||||
"color": "#F59E0B",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["CI/CD pipeline design", "Infrastructure automation", "Monitoring setup", "Deployment optimization"],
|
||||
"collaboration_hints": ["backend-engineer", "security-engineer", "mlops-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Security Engineer",
|
||||
@@ -467,7 +539,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "quality",
|
||||
"icon": "shield-check",
|
||||
"color": "#10B981",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Security architecture", "Vulnerability assessment", "Compliance validation", "Threat modeling"],
|
||||
"collaboration_hints": ["solutions-architect", "devops-engineer", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "AI/ML Engineer",
|
||||
@@ -503,7 +581,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "ai_ml",
|
||||
"icon": "brain",
|
||||
"color": "#8B5CF6",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Model development", "Algorithm selection", "Feature engineering", "Model optimization"],
|
||||
"collaboration_hints": ["data-scientist", "mlops-engineer", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "AI Researcher",
|
||||
@@ -537,7 +621,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "ai_ml",
|
||||
"icon": "microscope",
|
||||
"color": "#8B5CF6",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Research paper analysis", "Novel algorithm design", "Experiment design", "Benchmark evaluation"],
|
||||
"collaboration_hints": ["ai-ml-engineer", "data-scientist", "scientific-computing-expert"]
|
||||
},
|
||||
{
|
||||
"name": "Computer Vision Engineer",
|
||||
@@ -573,7 +663,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "ai_ml",
|
||||
"icon": "eye",
|
||||
"color": "#8B5CF6",
|
||||
"sort_order": 30,
|
||||
"typical_tasks": ["Image processing pipelines", "Object detection models", "Video analysis", "Computer vision deployment"],
|
||||
"collaboration_hints": ["ai-ml-engineer", "mlops-engineer", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "NLP Engineer",
|
||||
@@ -609,7 +705,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "ai_ml",
|
||||
"icon": "message-square",
|
||||
"color": "#8B5CF6",
|
||||
"sort_order": 40,
|
||||
"typical_tasks": ["Text processing pipelines", "Language model fine-tuning", "Named entity recognition", "Sentiment analysis"],
|
||||
"collaboration_hints": ["ai-ml-engineer", "data-scientist", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "MLOps Engineer",
|
||||
@@ -645,7 +747,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_release"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "operations",
|
||||
"icon": "settings-2",
|
||||
"color": "#F59E0B",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["ML pipeline development", "Model deployment", "Feature store management", "Model monitoring"],
|
||||
"collaboration_hints": ["ai-ml-engineer", "devops-engineer", "data-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Data Scientist",
|
||||
@@ -681,7 +789,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "data",
|
||||
"icon": "chart-bar",
|
||||
"color": "#06B6D4",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Statistical analysis", "Predictive modeling", "Data visualization", "Insight generation"],
|
||||
"collaboration_hints": ["data-engineer", "ai-ml-engineer", "business-analyst"]
|
||||
},
|
||||
{
|
||||
"name": "Data Engineer",
|
||||
@@ -717,7 +831,13 @@
|
||||
"denied": [],
|
||||
"require_approval": ["gitea:create_pull_request"]
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "data",
|
||||
"icon": "database",
|
||||
"color": "#06B6D4",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Data pipeline development", "ETL optimization", "Data warehouse design", "Data quality management"],
|
||||
"collaboration_hints": ["data-scientist", "backend-engineer", "mlops-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Technical Lead",
|
||||
@@ -749,7 +869,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "leadership",
|
||||
"icon": "users",
|
||||
"color": "#F97316",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Technical direction", "Code review leadership", "Team mentoring", "Architecture decisions"],
|
||||
"collaboration_hints": ["solutions-architect", "backend-engineer", "frontend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Scrum Master",
|
||||
@@ -781,7 +907,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "leadership",
|
||||
"icon": "target",
|
||||
"color": "#F97316",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Sprint facilitation", "Impediment removal", "Process improvement", "Team coaching"],
|
||||
"collaboration_hints": ["project-manager", "product-owner", "technical-lead"]
|
||||
},
|
||||
{
|
||||
"name": "Financial Systems Expert",
|
||||
@@ -816,7 +948,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "domain_expert",
|
||||
"icon": "calculator",
|
||||
"color": "#84CC16",
|
||||
"sort_order": 10,
|
||||
"typical_tasks": ["Financial system design", "Regulatory compliance", "Transaction processing", "Audit trail implementation"],
|
||||
"collaboration_hints": ["solutions-architect", "security-engineer", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Healthcare Systems Expert",
|
||||
@@ -850,7 +988,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "domain_expert",
|
||||
"icon": "heart-pulse",
|
||||
"color": "#84CC16",
|
||||
"sort_order": 20,
|
||||
"typical_tasks": ["Healthcare system design", "HIPAA compliance", "HL7/FHIR integration", "Clinical workflow optimization"],
|
||||
"collaboration_hints": ["solutions-architect", "security-engineer", "data-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Scientific Computing Expert",
|
||||
@@ -886,7 +1030,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "domain_expert",
|
||||
"icon": "flask",
|
||||
"color": "#84CC16",
|
||||
"sort_order": 30,
|
||||
"typical_tasks": ["HPC architecture", "Scientific algorithm implementation", "Data pipeline optimization", "Numerical computing"],
|
||||
"collaboration_hints": ["ai-researcher", "data-scientist", "backend-engineer"]
|
||||
},
|
||||
{
|
||||
"name": "Behavioral Psychology Expert",
|
||||
@@ -919,7 +1069,13 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "domain_expert",
|
||||
"icon": "lightbulb",
|
||||
"color": "#84CC16",
|
||||
"sort_order": 40,
|
||||
"typical_tasks": ["Behavioral design", "Engagement optimization", "User motivation analysis", "Ethical AI guidelines"],
|
||||
"collaboration_hints": ["ux-researcher", "ui-ux-designer", "product-owner"]
|
||||
},
|
||||
{
|
||||
"name": "Technical Writer",
|
||||
@@ -951,6 +1107,12 @@
|
||||
"denied": [],
|
||||
"require_approval": []
|
||||
},
|
||||
"is_active": true
|
||||
"is_active": true,
|
||||
"category": "domain_expert",
|
||||
"icon": "book-open",
|
||||
"color": "#84CC16",
|
||||
"sort_order": 50,
|
||||
"typical_tasks": ["API documentation", "User guides", "Technical specifications", "Knowledge base creation"],
|
||||
"collaboration_hints": ["solutions-architect", "product-owner", "qa-engineer"]
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user