package council import ( "context" "encoding/json" "fmt" "strings" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "github.com/rs/zerolog/log" ) // CouncilComposer manages the formation and orchestration of project kickoff councils type CouncilComposer struct { db *pgxpool.Pool ctx context.Context cancel context.CancelFunc } // NewCouncilComposer creates a new council composer service func NewCouncilComposer(db *pgxpool.Pool) *CouncilComposer { ctx, cancel := context.WithCancel(context.Background()) return &CouncilComposer{ db: db, ctx: ctx, cancel: cancel, } } // Close shuts down the council composer func (cc *CouncilComposer) Close() error { cc.cancel() return nil } // FormCouncil creates a council composition for a project kickoff func (cc *CouncilComposer) FormCouncil(ctx context.Context, request *CouncilFormationRequest) (*CouncilComposition, error) { startTime := time.Now() councilID := uuid.New() log.Info(). Str("council_id", councilID.String()). Str("project_name", request.ProjectName). Str("repository", request.Repository). Msg("🎭 Forming project kickoff council") // Create core council agents (always required) coreAgents := make([]CouncilAgent, len(CoreCouncilRoles)) for i, roleName := range CoreCouncilRoles { agentID := fmt.Sprintf("council-%s-%s", strings.ReplaceAll(request.ProjectName, " ", "-"), roleName) coreAgents[i] = CouncilAgent{ AgentID: agentID, RoleName: roleName, AgentName: cc.formatRoleName(roleName), Required: true, Deployed: false, Status: "pending", } } // Determine optional agents based on project characteristics optionalAgents := cc.selectOptionalAgents(request) // Create council composition composition := &CouncilComposition{ CouncilID: councilID, ProjectName: request.ProjectName, CoreAgents: coreAgents, OptionalAgents: optionalAgents, CreatedAt: startTime, Status: "forming", } // Store council composition in database err := cc.storeCouncilComposition(ctx, composition, request) if err != nil { return nil, fmt.Errorf("failed to store council composition: %w", err) } log.Info(). Str("council_id", councilID.String()). Int("core_agents", len(coreAgents)). Int("optional_agents", len(optionalAgents)). Dur("formation_time", time.Since(startTime)). Msg("✅ Council composition formed") return composition, nil } // selectOptionalAgents determines which optional council agents should be included func (cc *CouncilComposer) selectOptionalAgents(request *CouncilFormationRequest) []CouncilAgent { var selectedAgents []CouncilAgent // Analyze project brief and characteristics to determine needed optional roles brief := strings.ToLower(request.ProjectBrief) // Data/AI projects if strings.Contains(brief, "ai") || strings.Contains(brief, "machine learning") || strings.Contains(brief, "data") || strings.Contains(brief, "analytics") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("data-ai-architect", request.ProjectName)) } // Privacy/compliance sensitive projects if strings.Contains(brief, "privacy") || strings.Contains(brief, "personal data") || strings.Contains(brief, "gdpr") || strings.Contains(brief, "compliance") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("privacy-data-governance-officer", request.ProjectName)) } // Regulated industries if strings.Contains(brief, "healthcare") || strings.Contains(brief, "finance") || strings.Contains(brief, "banking") || strings.Contains(brief, "regulated") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("compliance-legal-liaison", request.ProjectName)) } // Performance-critical systems if strings.Contains(brief, "performance") || strings.Contains(brief, "high-load") || strings.Contains(brief, "scale") || strings.Contains(brief, "benchmark") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("performance-benchmarking-analyst", request.ProjectName)) } // User-facing applications if strings.Contains(brief, "user interface") || strings.Contains(brief, "ui") || strings.Contains(brief, "ux") || strings.Contains(brief, "frontend") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("ui-ux-designer", request.ProjectName)) } // Mobile applications if strings.Contains(brief, "mobile") || strings.Contains(brief, "ios") || strings.Contains(brief, "android") || strings.Contains(brief, "app store") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("ios-macos-developer", request.ProjectName)) } // Games or graphics-intensive applications if strings.Contains(brief, "game") || strings.Contains(brief, "graphics") || strings.Contains(brief, "rendering") || strings.Contains(brief, "3d") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("engine-programmer", request.ProjectName)) } // Integration-heavy projects if strings.Contains(brief, "integration") || strings.Contains(brief, "api") || strings.Contains(brief, "microservice") || strings.Contains(brief, "third-party") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("integration-architect", request.ProjectName)) } // Cost-sensitive or enterprise projects if strings.Contains(brief, "budget") || strings.Contains(brief, "cost") || strings.Contains(brief, "enterprise") || strings.Contains(brief, "licensing") { selectedAgents = append(selectedAgents, cc.createOptionalAgent("cost-licensing-steward", request.ProjectName)) } return selectedAgents } // createOptionalAgent creates an optional council agent func (cc *CouncilComposer) createOptionalAgent(roleName, projectName string) CouncilAgent { agentID := fmt.Sprintf("council-%s-%s", strings.ReplaceAll(projectName, " ", "-"), roleName) return CouncilAgent{ AgentID: agentID, RoleName: roleName, AgentName: cc.formatRoleName(roleName), Required: false, Deployed: false, Status: "pending", } } // formatRoleName converts role key to human-readable name func (cc *CouncilComposer) formatRoleName(roleName string) string { // Convert kebab-case to Title Case parts := strings.Split(roleName, "-") for i, part := range parts { parts[i] = strings.Title(part) } return strings.Join(parts, " ") } // storeCouncilComposition stores the council composition in the database func (cc *CouncilComposer) storeCouncilComposition(ctx context.Context, composition *CouncilComposition, request *CouncilFormationRequest) error { // Store council metadata councilQuery := ` INSERT INTO councils (id, project_name, repository, project_brief, status, created_at, task_id, issue_id, external_url, metadata) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ` metadataJSON, _ := json.Marshal(request.Metadata) _, err := cc.db.Exec(ctx, councilQuery, composition.CouncilID, composition.ProjectName, request.Repository, request.ProjectBrief, composition.Status, composition.CreatedAt, request.TaskID, request.IssueID, request.ExternalURL, metadataJSON, ) if err != nil { return fmt.Errorf("failed to store council metadata: %w", err) } // Store council agents for _, agent := range composition.CoreAgents { err = cc.storeCouncilAgent(ctx, composition.CouncilID, agent) if err != nil { return fmt.Errorf("failed to store core agent %s: %w", agent.AgentID, err) } } for _, agent := range composition.OptionalAgents { err = cc.storeCouncilAgent(ctx, composition.CouncilID, agent) if err != nil { return fmt.Errorf("failed to store optional agent %s: %w", agent.AgentID, err) } } return nil } // storeCouncilAgent stores a single council agent in the database func (cc *CouncilComposer) storeCouncilAgent(ctx context.Context, councilID uuid.UUID, agent CouncilAgent) error { query := ` INSERT INTO council_agents (council_id, agent_id, role_name, agent_name, required, deployed, status, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) ` _, err := cc.db.Exec(ctx, query, councilID, agent.AgentID, agent.RoleName, agent.AgentName, agent.Required, agent.Deployed, agent.Status, ) return err } // GetCouncilComposition retrieves a council composition by ID func (cc *CouncilComposer) GetCouncilComposition(ctx context.Context, councilID uuid.UUID) (*CouncilComposition, error) { // Implementation would query the database and reconstruct the composition // For now, return a simple error return nil, fmt.Errorf("not implemented yet") } // UpdateCouncilStatus updates the status of a council func (cc *CouncilComposer) UpdateCouncilStatus(ctx context.Context, councilID uuid.UUID, status string) error { query := `UPDATE councils SET status = $1, updated_at = NOW() WHERE id = $2` _, err := cc.db.Exec(ctx, query, status, councilID) return err }