feat: Production readiness improvements for WHOOSH council formation
Major security, observability, and configuration improvements:
## Security Hardening
- Implemented configurable CORS (no more wildcards)
- Added comprehensive auth middleware for admin endpoints
- Enhanced webhook HMAC validation
- Added input validation and rate limiting
- Security headers and CSP policies
## Configuration Management
- Made N8N webhook URL configurable (WHOOSH_N8N_BASE_URL)
- Replaced all hardcoded endpoints with environment variables
- Added feature flags for LLM vs heuristic composition
- Gitea fetch hardening with EAGER_FILTER and FULL_RESCAN options
## API Completeness
- Implemented GetCouncilComposition function
- Added GET /api/v1/councils/{id} endpoint
- Council artifacts API (POST/GET /api/v1/councils/{id}/artifacts)
- /admin/health/details endpoint with component status
- Database lookup for repository URLs (no hardcoded fallbacks)
## Observability & Performance
- Added OpenTelemetry distributed tracing with goal/pulse correlation
- Performance optimization database indexes
- Comprehensive health monitoring
- Enhanced logging and error handling
## Infrastructure
- Production-ready P2P discovery (replaces mock implementation)
- Removed unused Redis configuration
- Enhanced Docker Swarm integration
- Added migration files for performance indexes
## Code Quality
- Comprehensive input validation
- Graceful error handling and failsafe fallbacks
- Backwards compatibility maintained
- Following security best practices
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,9 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/rs/zerolog/log"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/chorus-services/whoosh/internal/tracing"
|
||||
)
|
||||
|
||||
// CouncilComposer manages the formation and orchestration of project kickoff councils
|
||||
@@ -38,9 +41,28 @@ func (cc *CouncilComposer) Close() error {
|
||||
|
||||
// FormCouncil creates a council composition for a project kickoff
|
||||
func (cc *CouncilComposer) FormCouncil(ctx context.Context, request *CouncilFormationRequest) (*CouncilComposition, error) {
|
||||
ctx, span := tracing.StartCouncilSpan(ctx, "form_council", "")
|
||||
defer span.End()
|
||||
|
||||
startTime := time.Now()
|
||||
councilID := uuid.New()
|
||||
|
||||
// Add tracing attributes
|
||||
span.SetAttributes(
|
||||
attribute.String("council.id", councilID.String()),
|
||||
attribute.String("project.name", request.ProjectName),
|
||||
attribute.String("repository.name", request.Repository),
|
||||
attribute.String("project.brief", request.ProjectBrief),
|
||||
)
|
||||
|
||||
// Add goal.id and pulse.id if available in the request
|
||||
if request.GoalID != "" {
|
||||
span.SetAttributes(attribute.String("goal.id", request.GoalID))
|
||||
}
|
||||
if request.PulseID != "" {
|
||||
span.SetAttributes(attribute.String("pulse.id", request.PulseID))
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("council_id", councilID.String()).
|
||||
Str("project_name", request.ProjectName).
|
||||
@@ -77,9 +99,19 @@ func (cc *CouncilComposer) FormCouncil(ctx context.Context, request *CouncilForm
|
||||
// Store council composition in database
|
||||
err := cc.storeCouncilComposition(ctx, composition, request)
|
||||
if err != nil {
|
||||
tracing.SetSpanError(span, err)
|
||||
span.SetAttributes(attribute.String("council.formation.status", "failed"))
|
||||
return nil, fmt.Errorf("failed to store council composition: %w", err)
|
||||
}
|
||||
|
||||
// Add success metrics to span
|
||||
span.SetAttributes(
|
||||
attribute.Int("council.core_agents.count", len(coreAgents)),
|
||||
attribute.Int("council.optional_agents.count", len(optionalAgents)),
|
||||
attribute.Int64("council.formation.duration_ms", time.Since(startTime).Milliseconds()),
|
||||
attribute.String("council.formation.status", "completed"),
|
||||
)
|
||||
|
||||
log.Info().
|
||||
Str("council_id", councilID.String()).
|
||||
Int("core_agents", len(coreAgents)).
|
||||
@@ -244,9 +276,91 @@ func (cc *CouncilComposer) storeCouncilAgent(ctx context.Context, councilID uuid
|
||||
|
||||
// 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")
|
||||
// First, get the council metadata
|
||||
councilQuery := `
|
||||
SELECT id, project_name, status, created_at
|
||||
FROM councils
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
var composition CouncilComposition
|
||||
var status string
|
||||
var createdAt time.Time
|
||||
|
||||
err := cc.db.QueryRow(ctx, councilQuery, councilID).Scan(
|
||||
&composition.CouncilID,
|
||||
&composition.ProjectName,
|
||||
&status,
|
||||
&createdAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query council: %w", err)
|
||||
}
|
||||
|
||||
composition.Status = status
|
||||
composition.CreatedAt = createdAt
|
||||
|
||||
// Get all agents for this council
|
||||
agentQuery := `
|
||||
SELECT agent_id, role_name, agent_name, required, deployed, status, deployed_at
|
||||
FROM council_agents
|
||||
WHERE council_id = $1
|
||||
ORDER BY required DESC, role_name ASC
|
||||
`
|
||||
|
||||
rows, err := cc.db.Query(ctx, agentQuery, councilID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query council agents: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Separate core and optional agents
|
||||
var coreAgents []CouncilAgent
|
||||
var optionalAgents []CouncilAgent
|
||||
|
||||
for rows.Next() {
|
||||
var agent CouncilAgent
|
||||
var deployedAt *time.Time
|
||||
|
||||
err := rows.Scan(
|
||||
&agent.AgentID,
|
||||
&agent.RoleName,
|
||||
&agent.AgentName,
|
||||
&agent.Required,
|
||||
&agent.Deployed,
|
||||
&agent.Status,
|
||||
&deployedAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan agent row: %w", err)
|
||||
}
|
||||
|
||||
agent.DeployedAt = deployedAt
|
||||
|
||||
if agent.Required {
|
||||
coreAgents = append(coreAgents, agent)
|
||||
} else {
|
||||
optionalAgents = append(optionalAgents, agent)
|
||||
}
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error iterating agent rows: %w", err)
|
||||
}
|
||||
|
||||
composition.CoreAgents = coreAgents
|
||||
composition.OptionalAgents = optionalAgents
|
||||
|
||||
log.Info().
|
||||
Str("council_id", councilID.String()).
|
||||
Str("project_name", composition.ProjectName).
|
||||
Int("core_agents", len(coreAgents)).
|
||||
Int("optional_agents", len(optionalAgents)).
|
||||
Msg("Retrieved council composition")
|
||||
|
||||
return &composition, nil
|
||||
}
|
||||
|
||||
// UpdateCouncilStatus updates the status of a council
|
||||
|
||||
Reference in New Issue
Block a user