Harden CHORUS security and messaging stack

This commit is contained in:
anthonyrawlins
2025-09-20 23:21:35 +10:00
parent 57751f277a
commit 1bb736c09a
25 changed files with 2793 additions and 2474 deletions

View File

@@ -9,50 +9,57 @@ import (
"chorus/internal/logging"
"chorus/pkg/config"
"chorus/pubsub"
"chorus/pkg/repository"
"chorus/pkg/hmmm"
"chorus/pkg/repository"
"chorus/pubsub"
"github.com/google/uuid"
"github.com/libp2p/go-libp2p/core/peer"
)
// TaskProgressTracker is notified when tasks start and complete so availability broadcasts stay accurate.
type TaskProgressTracker interface {
AddTask(taskID string)
RemoveTask(taskID string)
}
// TaskCoordinator manages task discovery, assignment, and execution across multiple repositories
type TaskCoordinator struct {
pubsub *pubsub.PubSub
hlog *logging.HypercoreLog
ctx context.Context
config *config.Config
hmmmRouter *hmmm.Router
pubsub *pubsub.PubSub
hlog *logging.HypercoreLog
ctx context.Context
config *config.Config
hmmmRouter *hmmm.Router
// Repository management
providers map[int]repository.TaskProvider // projectID -> provider
providerLock sync.RWMutex
factory repository.ProviderFactory
providers map[int]repository.TaskProvider // projectID -> provider
providerLock sync.RWMutex
factory repository.ProviderFactory
// Task management
activeTasks map[string]*ActiveTask // taskKey -> active task
taskLock sync.RWMutex
taskMatcher repository.TaskMatcher
activeTasks map[string]*ActiveTask // taskKey -> active task
taskLock sync.RWMutex
taskMatcher repository.TaskMatcher
taskTracker TaskProgressTracker
// Agent tracking
nodeID string
agentInfo *repository.AgentInfo
nodeID string
agentInfo *repository.AgentInfo
// Sync settings
syncInterval time.Duration
lastSync map[int]time.Time
syncLock sync.RWMutex
syncInterval time.Duration
lastSync map[int]time.Time
syncLock sync.RWMutex
}
// ActiveTask represents a task currently being worked on
type ActiveTask struct {
Task *repository.Task
Provider repository.TaskProvider
ProjectID int
ClaimedAt time.Time
Status string // claimed, working, completed, failed
AgentID string
Results map[string]interface{}
Task *repository.Task
Provider repository.TaskProvider
ProjectID int
ClaimedAt time.Time
Status string // claimed, working, completed, failed
AgentID string
Results map[string]interface{}
}
// NewTaskCoordinator creates a new task coordinator
@@ -63,7 +70,9 @@ func NewTaskCoordinator(
cfg *config.Config,
nodeID string,
hmmmRouter *hmmm.Router,
tracker TaskProgressTracker,
) *TaskCoordinator {
coordinator := &TaskCoordinator{
pubsub: ps,
hlog: hlog,
@@ -75,10 +84,11 @@ func NewTaskCoordinator(
lastSync: make(map[int]time.Time),
factory: &repository.DefaultProviderFactory{},
taskMatcher: &repository.DefaultTaskMatcher{},
taskTracker: tracker,
nodeID: nodeID,
syncInterval: 30 * time.Second,
}
// Create agent info from config
coordinator.agentInfo = &repository.AgentInfo{
ID: cfg.Agent.ID,
@@ -91,23 +101,23 @@ func NewTaskCoordinator(
Performance: map[string]interface{}{"score": 0.8}, // Default performance score
Availability: "available",
}
return coordinator
}
// Start begins the task coordination process
func (tc *TaskCoordinator) Start() {
fmt.Printf("🎯 Starting task coordinator for agent %s (%s)\n", tc.agentInfo.ID, tc.agentInfo.Role)
// Announce role and capabilities
tc.announceAgentRole()
// Start periodic task discovery and sync
go tc.taskDiscoveryLoop()
// Start role-based message handling
tc.pubsub.SetAntennaeMessageHandler(tc.handleRoleMessage)
fmt.Printf("✅ Task coordinator started\n")
}
@@ -185,13 +195,17 @@ func (tc *TaskCoordinator) processTask(task *repository.Task, provider repositor
tc.agentInfo.CurrentTasks = len(tc.activeTasks)
tc.taskLock.Unlock()
if tc.taskTracker != nil {
tc.taskTracker.AddTask(taskKey)
}
// Log task claim
tc.hlog.Append(logging.TaskClaimed, map[string]interface{}{
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"required_role": task.RequiredRole,
"priority": task.Priority,
"priority": task.Priority,
})
// Announce task claim
@@ -212,11 +226,11 @@ func (tc *TaskCoordinator) processTask(task *repository.Task, provider repositor
}
if err := tc.hmmmRouter.Publish(tc.ctx, seedMsg); err != nil {
fmt.Printf("⚠️ Failed to seed HMMM room for task %d: %v\n", task.Number, err)
tc.hlog.AppendString("system_error", map[string]interface{}{
"error": "hmmm_seed_failed",
"task_number": task.Number,
"repository": task.Repository,
"message": err.Error(),
tc.hlog.AppendString("system_error", map[string]interface{}{
"error": "hmmm_seed_failed",
"task_number": task.Number,
"repository": task.Repository,
"message": err.Error(),
})
} else {
fmt.Printf("🐜 Seeded HMMM room for task %d\n", task.Number)
@@ -259,14 +273,14 @@ func (tc *TaskCoordinator) shouldRequestCollaboration(task *repository.Task) boo
// requestTaskCollaboration requests collaboration for a task
func (tc *TaskCoordinator) requestTaskCollaboration(task *repository.Task) {
data := map[string]interface{}{
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"required_role": task.RequiredRole,
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"required_role": task.RequiredRole,
"required_expertise": task.RequiredExpertise,
"priority": task.Priority,
"requester_role": tc.agentInfo.Role,
"reason": "expertise_gap",
"priority": task.Priority,
"requester_role": tc.agentInfo.Role,
"reason": "expertise_gap",
}
opts := pubsub.MessageOptions{
@@ -288,7 +302,7 @@ func (tc *TaskCoordinator) requestTaskCollaboration(task *repository.Task) {
// executeTask executes a claimed task
func (tc *TaskCoordinator) executeTask(activeTask *ActiveTask) {
taskKey := fmt.Sprintf("%s:%d", activeTask.Task.Repository, activeTask.Task.Number)
// Update status
tc.taskLock.Lock()
activeTask.Status = "working"
@@ -302,10 +316,10 @@ func (tc *TaskCoordinator) executeTask(activeTask *ActiveTask) {
// Complete the task
results := map[string]interface{}{
"status": "completed",
"status": "completed",
"completion_time": time.Now().Format(time.RFC3339),
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
}
taskResult := &repository.TaskResult{
@@ -316,13 +330,13 @@ func (tc *TaskCoordinator) executeTask(activeTask *ActiveTask) {
err := activeTask.Provider.CompleteTask(activeTask.Task, taskResult)
if err != nil {
fmt.Printf("❌ Failed to complete task %s #%d: %v\n", activeTask.Task.Repository, activeTask.Task.Number, err)
// Update status to failed
tc.taskLock.Lock()
activeTask.Status = "failed"
activeTask.Results = map[string]interface{}{"error": err.Error()}
tc.taskLock.Unlock()
return
}
@@ -334,6 +348,10 @@ func (tc *TaskCoordinator) executeTask(activeTask *ActiveTask) {
tc.agentInfo.CurrentTasks = len(tc.activeTasks)
tc.taskLock.Unlock()
if tc.taskTracker != nil {
tc.taskTracker.RemoveTask(taskKey)
}
// Log completion
tc.hlog.Append(logging.TaskCompleted, map[string]interface{}{
"task_number": activeTask.Task.Number,
@@ -378,19 +396,19 @@ func (tc *TaskCoordinator) announceAgentRole() {
// announceTaskClaim announces that this agent has claimed a task
func (tc *TaskCoordinator) announceTaskClaim(task *repository.Task) {
data := map[string]interface{}{
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
"claim_time": time.Now().Format(time.RFC3339),
"task_number": task.Number,
"repository": task.Repository,
"title": task.Title,
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
"claim_time": time.Now().Format(time.RFC3339),
"estimated_completion": time.Now().Add(time.Hour).Format(time.RFC3339),
}
opts := pubsub.MessageOptions{
FromRole: tc.agentInfo.Role,
Priority: "medium",
ThreadID: fmt.Sprintf("task-%s-%d", task.Repository, task.Number),
FromRole: tc.agentInfo.Role,
Priority: "medium",
ThreadID: fmt.Sprintf("task-%s-%d", task.Repository, task.Number),
}
err := tc.pubsub.PublishRoleBasedMessage(pubsub.TaskProgress, data, opts)
@@ -463,15 +481,15 @@ func (tc *TaskCoordinator) handleTaskHelpRequest(msg pubsub.Message, from peer.I
}
}
if canHelp && tc.agentInfo.CurrentTasks < tc.agentInfo.MaxTasks {
if canHelp && tc.agentInfo.CurrentTasks < tc.agentInfo.MaxTasks {
// Offer help
responseData := map[string]interface{}{
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
"expertise": tc.agentInfo.Expertise,
"availability": tc.agentInfo.MaxTasks - tc.agentInfo.CurrentTasks,
"offer_type": "collaboration",
"response_to": msg.Data,
"agent_id": tc.agentInfo.ID,
"agent_role": tc.agentInfo.Role,
"expertise": tc.agentInfo.Expertise,
"availability": tc.agentInfo.MaxTasks - tc.agentInfo.CurrentTasks,
"offer_type": "collaboration",
"response_to": msg.Data,
}
opts := pubsub.MessageOptions{
@@ -480,34 +498,34 @@ func (tc *TaskCoordinator) handleTaskHelpRequest(msg pubsub.Message, from peer.I
ThreadID: msg.ThreadID,
}
err := tc.pubsub.PublishRoleBasedMessage(pubsub.TaskHelpResponse, responseData, opts)
if err != nil {
fmt.Printf("⚠️ Failed to offer help: %v\n", err)
} else {
fmt.Printf("🤝 Offered help for task collaboration\n")
}
err := tc.pubsub.PublishRoleBasedMessage(pubsub.TaskHelpResponse, responseData, opts)
if err != nil {
fmt.Printf("⚠️ Failed to offer help: %v\n", err)
} else {
fmt.Printf("🤝 Offered help for task collaboration\n")
}
// Also reflect the help offer into the HMMM per-issue room (best-effort)
if tc.hmmmRouter != nil {
if tn, ok := msg.Data["task_number"].(float64); ok {
issueID := int64(tn)
hmsg := hmmm.Message{
Version: 1,
Type: "meta_msg",
IssueID: issueID,
ThreadID: fmt.Sprintf("issue-%d", issueID),
MsgID: uuid.New().String(),
NodeID: tc.nodeID,
HopCount: 0,
Timestamp: time.Now().UTC(),
Message: fmt.Sprintf("Help offer from %s (availability %d)", tc.agentInfo.Role, tc.agentInfo.MaxTasks-tc.agentInfo.CurrentTasks),
}
if err := tc.hmmmRouter.Publish(tc.ctx, hmsg); err != nil {
fmt.Printf("⚠️ Failed to reflect help into HMMM: %v\n", err)
}
}
}
}
// Also reflect the help offer into the HMMM per-issue room (best-effort)
if tc.hmmmRouter != nil {
if tn, ok := msg.Data["task_number"].(float64); ok {
issueID := int64(tn)
hmsg := hmmm.Message{
Version: 1,
Type: "meta_msg",
IssueID: issueID,
ThreadID: fmt.Sprintf("issue-%d", issueID),
MsgID: uuid.New().String(),
NodeID: tc.nodeID,
HopCount: 0,
Timestamp: time.Now().UTC(),
Message: fmt.Sprintf("Help offer from %s (availability %d)", tc.agentInfo.Role, tc.agentInfo.MaxTasks-tc.agentInfo.CurrentTasks),
}
if err := tc.hmmmRouter.Publish(tc.ctx, hmsg); err != nil {
fmt.Printf("⚠️ Failed to reflect help into HMMM: %v\n", err)
}
}
}
}
}
// handleExpertiseRequest handles requests for specific expertise