Refactor subtasks
Change-Id: I5ea6ffe266b8d6010de46bbf3bc6d7f861600f00
diff --git a/server/agent/manager.go b/server/agent/manager.go
index c206665..a302133 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -10,27 +10,26 @@
"strings"
"time"
- "github.com/iomodo/staff/assignment"
"github.com/iomodo/staff/config"
"github.com/iomodo/staff/git"
"github.com/iomodo/staff/llm"
_ "github.com/iomodo/staff/llm/providers" // Auto-register all providers
- "github.com/iomodo/staff/subtasks"
+ "github.com/iomodo/staff/task"
"github.com/iomodo/staff/tm"
)
// Manager manages multiple AI agents with Git operations and task processing
type Manager struct {
- config *config.Config
- agents map[string]*Agent
- taskManager tm.TaskManager
- autoAssigner *assignment.AutoAssigner
- prProvider git.PullRequestProvider
- cloneManager *git.CloneManager
- subtaskService *subtasks.SubtaskService
- isRunning map[string]bool
- stopChannels map[string]chan struct{}
- logger *slog.Logger
+ config *config.Config
+ agents map[string]*Agent
+ taskManager tm.TaskManager
+ autoAssigner *task.AutoAssigner
+ prProvider git.PullRequestProvider
+ cloneManager *git.CloneManager
+ subtaskService *task.SubtaskService
+ isRunning map[string]bool
+ stopChannels map[string]chan struct{}
+ logger *slog.Logger
}
// NewManager creates a new agent manager
@@ -39,12 +38,12 @@
logger = slog.Default()
}
// Create auto-assigner
- autoAssigner := assignment.NewAutoAssigner(cfg.Agents)
+ autoAssigner := task.NewAutoAssigner(cfg.Agents)
// Create PR provider based on configuration
var prProvider git.PullRequestProvider
var repoURL string
-
+
switch cfg.GetPrimaryGitProvider() {
case "github":
githubConfig := git.GitHubConfig{
@@ -53,8 +52,8 @@
}
prProvider = git.NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
repoURL = fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
- logger.Info("Using GitHub as pull request provider",
- slog.String("owner", cfg.GitHub.Owner),
+ logger.Info("Using GitHub as pull request provider",
+ slog.String("owner", cfg.GitHub.Owner),
slog.String("repo", cfg.GitHub.Repo))
case "gerrit":
gerritConfig := git.GerritConfig{
@@ -65,14 +64,14 @@
}
prProvider = git.NewGerritPullRequestProvider(cfg.Gerrit.Project, gerritConfig)
repoURL = fmt.Sprintf("%s/%s", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
- logger.Info("Using Gerrit as pull request provider",
- slog.String("base_url", cfg.Gerrit.BaseURL),
+ logger.Info("Using Gerrit as pull request provider",
+ slog.String("base_url", cfg.Gerrit.BaseURL),
slog.String("project", cfg.Gerrit.Project))
default:
return nil, fmt.Errorf("no valid Git provider configured")
}
- // Create clone manager for per-agent Git repositories
+ // Create clone manager for per-agent Git repositories
workspacePath := filepath.Join(".", "workspace")
cloneManager := git.NewCloneManager(repoURL, workspacePath)
@@ -142,8 +141,8 @@
owner = m.config.Gerrit.Project
repo = m.config.Gerrit.Project
}
-
- m.subtaskService = subtasks.NewSubtaskService(
+
+ m.subtaskService = task.NewSubtaskService(
firstAgent.Provider,
m.taskManager,
agentRoles,
@@ -218,9 +217,9 @@
go m.runAgentLoop(agent, loopInterval, stopChan)
- m.logger.Info("Started agent",
- slog.String("name", agentName),
- slog.String("role", agent.Role),
+ m.logger.Info("Started agent",
+ slog.String("name", agentName),
+ slog.String("role", agent.Role),
slog.String("model", agent.Model))
return nil
}
@@ -251,8 +250,8 @@
return
case <-ticker.C:
if err := m.processAgentTasks(agent); err != nil {
- m.logger.Error("Error processing tasks for agent",
- slog.String("agent", agent.Name),
+ m.logger.Error("Error processing tasks for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
}
@@ -271,21 +270,21 @@
return fmt.Errorf("failed to get tasks for agent %s: %w", agent.Name, err)
}
- m.logger.Info("Processing tasks for agent",
- slog.Int("task_count", len(tasks)),
+ m.logger.Info("Processing tasks for agent",
+ slog.Int("task_count", len(tasks)),
slog.String("agent", agent.Name))
for _, task := range tasks {
if task.Status == tm.StatusToDo || task.Status == tm.StatusPending {
if err := m.processTask(agent, task); err != nil {
- m.logger.Error("Error processing task",
- slog.String("task_id", task.ID),
+ m.logger.Error("Error processing task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Mark task as failed
task.Status = tm.StatusFailed
if err := m.taskManager.UpdateTask(task); err != nil {
- m.logger.Error("Error updating failed task",
- slog.String("task_id", task.ID),
+ m.logger.Error("Error updating failed task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
}
agent.Stats.TasksFailed++
@@ -308,9 +307,9 @@
ctx := context.Background()
startTime := time.Now()
- m.logger.Info("Agent processing task",
- slog.String("agent", agent.Name),
- slog.String("task_id", task.ID),
+ m.logger.Info("Agent processing task",
+ slog.String("agent", agent.Name),
+ slog.String("task_id", task.ID),
slog.String("title", task.Title))
// Mark task as in progress
@@ -324,8 +323,8 @@
if m.shouldGenerateSubtasks(task) {
m.logger.Info("LLM determined task should generate subtasks", slog.String("task_id", task.ID))
if err := m.generateSubtasksForTask(ctx, task); err != nil {
- m.logger.Warn("Failed to generate subtasks for task",
- slog.String("task_id", task.ID),
+ m.logger.Warn("Failed to generate subtasks for task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Continue with normal processing if subtask generation fails
} else {
@@ -340,8 +339,8 @@
return fmt.Errorf("failed to update task with subtasks: %w", err)
}
- m.logger.Info("Task converted to subtasks by agent using LLM analysis",
- slog.String("task_id", task.ID),
+ m.logger.Info("Task converted to subtasks by agent using LLM analysis",
+ slog.String("task_id", task.ID),
slog.String("agent", agent.Name))
return nil
}
@@ -385,10 +384,10 @@
agent.Stats.AvgTime = (agent.Stats.AvgTime + duration.Milliseconds()) / 2
}
- m.logger.Info("Task completed by agent",
- slog.String("task_id", task.ID),
- slog.String("agent", agent.Name),
- slog.Duration("duration", duration),
+ m.logger.Info("Task completed by agent",
+ slog.String("task_id", task.ID),
+ slog.String("agent", agent.Name),
+ slog.Duration("duration", duration),
slog.String("pr_url", prURL))
return nil
}
@@ -478,14 +477,14 @@
return fmt.Errorf("failed to get agent clone: %w", err)
}
- m.logger.Info("Agent working in clone",
- slog.String("agent", agent.Name),
+ m.logger.Info("Agent working in clone",
+ slog.String("agent", agent.Name),
slog.String("clone_path", clonePath))
// Refresh the clone with latest changes
if err := m.cloneManager.RefreshAgentClone(agent.Name); err != nil {
- m.logger.Warn("Failed to refresh clone for agent",
- slog.String("agent", agent.Name),
+ m.logger.Warn("Failed to refresh clone for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
@@ -557,8 +556,8 @@
return fmt.Errorf("failed to push branch: %w", err)
}
- m.logger.Info("Agent successfully pushed branch",
- slog.String("agent", agent.Name),
+ m.logger.Info("Agent successfully pushed branch",
+ slog.String("agent", agent.Name),
slog.String("branch", branchName))
return nil
}
@@ -660,9 +659,9 @@
}
explanation := m.autoAssigner.GetRecommendationExplanation(task, agentName)
- m.logger.Info("Auto-assigned task to agent",
- slog.String("task_id", taskID),
- slog.String("agent", agentName),
+ m.logger.Info("Auto-assigned task to agent",
+ slog.String("task_id", taskID),
+ slog.String("agent", agentName),
slog.String("explanation", explanation))
return nil
@@ -711,8 +710,8 @@
ctx := context.Background()
decision, err := m.subtaskService.ShouldGenerateSubtasks(ctx, task)
if err != nil {
- m.logger.Warn("Failed to get LLM subtask decision for task",
- slog.String("task_id", task.ID),
+ m.logger.Warn("Failed to get LLM subtask decision for task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Fallback to simple heuristics
return task.Priority == tm.PriorityHigh || len(task.Description) > 200
@@ -724,10 +723,10 @@
m.logger.Warn("Failed to update task evaluation status", slog.String("error", err.Error()))
}
- m.logger.Info("LLM subtask decision for task",
- slog.String("task_id", task.ID),
- slog.Bool("needs_subtasks", decision.NeedsSubtasks),
- slog.Int("complexity_score", decision.ComplexityScore),
+ m.logger.Info("LLM subtask decision for task",
+ slog.String("task_id", task.ID),
+ slog.Bool("needs_subtasks", decision.NeedsSubtasks),
+ slog.Int("complexity_score", decision.ComplexityScore),
slog.String("reasoning", decision.Reasoning))
return decision.NeedsSubtasks
@@ -755,19 +754,19 @@
task.SubtasksPRURL = prURL
task.SubtasksGenerated = true
- m.logger.Info("Generated subtask PR for task",
- slog.String("task_id", task.ID),
+ m.logger.Info("Generated subtask PR for task",
+ slog.String("task_id", task.ID),
slog.String("pr_url", prURL))
- m.logger.Info("Proposed subtasks and new agents for task",
- slog.String("task_id", task.ID),
- slog.Int("subtask_count", len(analysis.Subtasks)),
+ m.logger.Info("Proposed subtasks and new agents for task",
+ slog.String("task_id", task.ID),
+ slog.Int("subtask_count", len(analysis.Subtasks)),
slog.Int("new_agent_count", len(analysis.AgentCreations)))
-
+
// Log proposed new agents if any
if len(analysis.AgentCreations) > 0 {
for _, agent := range analysis.AgentCreations {
- m.logger.Info("Proposed new agent",
- slog.String("role", agent.Role),
+ m.logger.Info("Proposed new agent",
+ slog.String("role", agent.Role),
slog.Any("skills", agent.Skills))
}
}
@@ -792,8 +791,8 @@
// Close all LLM providers
for _, agent := range m.agents {
if err := agent.Provider.Close(); err != nil {
- m.logger.Error("Error closing provider for agent",
- slog.String("agent", agent.Name),
+ m.logger.Error("Error closing provider for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
}
diff --git a/server/assignment/auto_assignment.go b/server/task/auto_assignment.go
similarity index 99%
rename from server/assignment/auto_assignment.go
rename to server/task/auto_assignment.go
index a5738c0..4bd5632 100644
--- a/server/assignment/auto_assignment.go
+++ b/server/task/auto_assignment.go
@@ -1,4 +1,4 @@
-package assignment
+package task
import (
"fmt"
diff --git a/server/subtasks/service.go b/server/task/service.go
similarity index 99%
rename from server/subtasks/service.go
rename to server/task/service.go
index d921006..ba6a4c4 100644
--- a/server/subtasks/service.go
+++ b/server/task/service.go
@@ -1,4 +1,4 @@
-package subtasks
+package task
import (
"context"
diff --git a/server/subtasks/service_test.go b/server/task/service_test.go
similarity index 98%
rename from server/subtasks/service_test.go
rename to server/task/service_test.go
index 08dd72a..5c7b337 100644
--- a/server/subtasks/service_test.go
+++ b/server/task/service_test.go
@@ -1,4 +1,4 @@
-package subtasks
+package task
import (
"context"
@@ -28,10 +28,10 @@
if m.callCount >= len(m.responses) {
return nil, nil
}
-
+
response := m.responses[m.callCount]
m.callCount++
-
+
return &llm.ChatCompletionResponse{
ID: "mock-response",
Object: "chat.completion",
@@ -80,17 +80,17 @@
func TestNewSubtaskService(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
agentRoles := []string{"backend", "frontend", "qa"}
-
+
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
if service == nil {
t.Fatal("NewSubtaskService returned nil")
}
-
+
if service.llmProvider != mockProvider {
t.Error("LLM provider not set correctly")
}
-
+
if len(service.agentRoles) != 3 {
t.Errorf("Expected 3 agent roles, got %d", len(service.agentRoles))
}
@@ -108,21 +108,21 @@
mockProvider := NewMockLLMProvider([]string{decisionResponse})
agentRoles := []string{"backend", "frontend", "qa"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
// Test the parseSubtaskDecision method directly since ShouldGenerateSubtasks is used by manager
decision, err := service.parseSubtaskDecision(decisionResponse)
if err != nil {
t.Fatalf("parseSubtaskDecision failed: %v", err)
}
-
+
if !decision.NeedsSubtasks {
t.Error("Expected decision to need subtasks")
}
-
+
if decision.ComplexityScore != 8 {
t.Errorf("Expected complexity score 8, got %d", decision.ComplexityScore)
}
-
+
if len(decision.RequiredSkills) != 3 {
t.Errorf("Expected 3 required skills, got %d", len(decision.RequiredSkills))
}
@@ -167,7 +167,7 @@
mockProvider := NewMockLLMProvider([]string{jsonResponse})
agentRoles := []string{"backend", "frontend", "qa", "ceo"} // Include CEO for agent creation
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
task := &tm.Task{
ID: "test-task-123",
Title: "Build authentication system",
@@ -177,27 +177,27 @@
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
-
+
analysis, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
if err != nil {
t.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
}
-
+
if analysis.ParentTaskID != task.ID {
t.Errorf("Expected parent task ID %s, got %s", task.ID, analysis.ParentTaskID)
}
-
+
if analysis.AnalysisSummary == "" {
t.Error("Analysis summary should not be empty")
}
-
+
// Should have 3 subtasks (1 for agent creation + 2 original)
if len(analysis.Subtasks) != 3 {
t.Errorf("Expected 3 subtasks (including agent creation), got %d", len(analysis.Subtasks))
t.Logf("Subtasks: %+v", analysis.Subtasks)
return // Exit early if count is wrong to avoid index errors
}
-
+
// Test agent creation was processed
if len(analysis.AgentCreations) != 1 {
t.Errorf("Expected 1 agent creation, got %d", len(analysis.AgentCreations))
@@ -239,7 +239,7 @@
if len(subtask1.RequiredSkills) != 2 {
t.Errorf("Expected 2 required skills, got %d", len(subtask1.RequiredSkills))
}
-
+
// Test third subtask (original frontend task, now at index 2 with updated dependencies)
subtask2 := analysis.Subtasks[2]
if subtask2.Title != "Frontend Development" {
@@ -255,7 +255,7 @@
if len(subtask2.RequiredSkills) != 2 {
t.Errorf("Expected 2 required skills, got %d", len(subtask2.RequiredSkills))
}
-
+
// Total hours should include agent creation time (4 hours)
if analysis.EstimatedTotalHours != 28 {
t.Errorf("Expected 28 total hours, got %d", analysis.EstimatedTotalHours)
@@ -264,21 +264,21 @@
func TestAnalyzeTaskForSubtasks_InvalidJSON(t *testing.T) {
invalidResponse := "This is not valid JSON"
-
+
mockProvider := NewMockLLMProvider([]string{invalidResponse})
agentRoles := []string{"backend", "frontend"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
task := &tm.Task{
ID: "test-task-123",
Title: "Test task",
}
-
+
_, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
if err == nil {
t.Error("Expected error for invalid JSON, got nil")
}
-
+
if !strings.Contains(err.Error(), "no JSON found") {
t.Errorf("Expected 'no JSON found' error, got: %v", err)
}
@@ -304,17 +304,17 @@
mockProvider := NewMockLLMProvider([]string{jsonResponse})
agentRoles := []string{"backend", "frontend"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
task := &tm.Task{
ID: "test-task-123",
Title: "Test task",
}
-
+
analysis, err := service.AnalyzeTaskForSubtasks(context.Background(), task)
if err != nil {
t.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
}
-
+
// Should fix invalid agent assignment to first available role
if analysis.Subtasks[0].AssignedTo != "backend" {
t.Errorf("Expected fixed assignment 'backend', got %s", analysis.Subtasks[0].AssignedTo)
@@ -324,7 +324,7 @@
func TestGenerateSubtaskPR(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
+
analysis := &tm.SubtaskAnalysis{
ParentTaskID: "task-123",
AnalysisSummary: "Test analysis summary",
@@ -342,13 +342,13 @@
},
},
}
-
+
// Test that PR generation fails when no PR provider is configured
_, err := service.GenerateSubtaskPR(context.Background(), analysis)
if err == nil {
t.Error("Expected error when PR provider not configured, got nil")
}
-
+
if !strings.Contains(err.Error(), "PR provider not configured") {
t.Errorf("Expected 'PR provider not configured' error, got: %v", err)
}
@@ -358,28 +358,28 @@
mockProvider := NewMockLLMProvider([]string{})
agentRoles := []string{"backend", "frontend", "qa"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
task := &tm.Task{
Title: "Build authentication system",
Description: "Implement user login and registration with OAuth",
Priority: tm.PriorityHigh,
Status: tm.StatusToDo,
}
-
+
prompt := service.buildSubtaskAnalysisPrompt(task)
-
+
if !strings.Contains(prompt, task.Title) {
t.Error("Prompt should contain task title")
}
-
+
if !strings.Contains(prompt, task.Description) {
t.Error("Prompt should contain task description")
}
-
+
if !strings.Contains(prompt, string(task.Priority)) {
t.Error("Prompt should contain task priority")
}
-
+
if !strings.Contains(prompt, string(task.Status)) {
t.Error("Prompt should contain task status")
}
@@ -389,21 +389,21 @@
mockProvider := NewMockLLMProvider([]string{})
agentRoles := []string{"backend", "frontend", "qa", "devops"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
systemPrompt := service.getSubtaskAnalysisSystemPrompt()
-
+
if !strings.Contains(systemPrompt, "backend") {
t.Error("System prompt should contain backend role")
}
-
+
if !strings.Contains(systemPrompt, "frontend") {
t.Error("System prompt should contain frontend role")
}
-
+
if !strings.Contains(systemPrompt, "JSON") {
t.Error("System prompt should mention JSON format")
}
-
+
if !strings.Contains(systemPrompt, "subtasks") {
t.Error("System prompt should mention subtasks")
}
@@ -413,19 +413,19 @@
mockProvider := NewMockLLMProvider([]string{})
agentRoles := []string{"backend", "frontend", "qa"}
service := NewSubtaskService(mockProvider, nil, agentRoles, nil, "example", "repo", nil, nil)
-
+
if !service.isValidAgentRole("backend") {
t.Error("'backend' should be a valid agent role")
}
-
+
if !service.isValidAgentRole("frontend") {
t.Error("'frontend' should be a valid agent role")
}
-
+
if service.isValidAgentRole("invalid") {
t.Error("'invalid' should not be a valid agent role")
}
-
+
if service.isValidAgentRole("") {
t.Error("Empty string should not be a valid agent role")
}
@@ -434,7 +434,7 @@
func TestParseSubtaskAnalysis_Priority(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
+
tests := []struct {
input string
expected tm.TaskPriority
@@ -451,7 +451,7 @@
{"invalid", tm.PriorityMedium}, // default
{"", tm.PriorityMedium}, // default
}
-
+
for _, test := range tests {
jsonResponse := `{
"analysis_summary": "Test",
@@ -466,18 +466,18 @@
"recommended_approach": "Test",
"estimated_total_hours": 8
}`
-
+
analysis, err := service.parseSubtaskAnalysis(jsonResponse, "test-task")
if err != nil {
t.Fatalf("parseSubtaskAnalysis failed for priority '%s': %v", test.input, err)
}
-
+
if len(analysis.Subtasks) != 1 {
t.Fatalf("Expected 1 subtask, got %d", len(analysis.Subtasks))
}
-
+
if analysis.Subtasks[0].Priority != test.expected {
- t.Errorf("For priority '%s', expected %s, got %s",
+ t.Errorf("For priority '%s', expected %s, got %s",
test.input, test.expected, analysis.Subtasks[0].Priority)
}
}
@@ -486,7 +486,7 @@
func TestGenerateSubtaskFile(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
+
subtask := tm.SubtaskProposal{
Title: "Implement API endpoints",
Description: "Create REST API endpoints for user management",
@@ -496,12 +496,12 @@
Dependencies: []string{"0"},
RequiredSkills: []string{"go", "rest_api"},
}
-
+
taskID := "parent-task-subtask-1"
parentTaskID := "parent-task"
-
+
content := service.generateSubtaskFile(subtask, taskID, parentTaskID)
-
+
// Verify YAML frontmatter
if !strings.Contains(content, "id: parent-task-subtask-1") {
t.Error("Generated file should contain task ID in frontmatter")
@@ -524,7 +524,7 @@
if !strings.Contains(content, "estimated_hours: 12") {
t.Error("Generated file should contain estimated hours")
}
-
+
// Verify dependencies are converted properly
if !strings.Contains(content, "dependencies:") {
t.Error("Generated file should contain dependencies section")
@@ -532,7 +532,7 @@
if !strings.Contains(content, "- parent-task-subtask-1") {
t.Error("Dependencies should be converted to subtask IDs")
}
-
+
// Verify required skills
if !strings.Contains(content, "required_skills:") {
t.Error("Generated file should contain required skills section")
@@ -540,7 +540,7 @@
if !strings.Contains(content, "- go") {
t.Error("Generated file should contain required skills")
}
-
+
// Verify markdown content
if !strings.Contains(content, "# Task Description") {
t.Error("Generated file should contain markdown task description")
@@ -553,7 +553,7 @@
func TestUpdateParentTaskAsCompleted(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
+
// Create a temporary task file for testing
taskContent := `---
id: test-task-123
@@ -571,18 +571,18 @@
A test task for validation
`
-
+
// Create temporary file
tmpFile, err := os.CreateTemp("", "test-task-*.md")
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
defer os.Remove(tmpFile.Name())
-
+
if err := os.WriteFile(tmpFile.Name(), []byte(taskContent), 0644); err != nil {
t.Fatalf("Failed to write temp file: %v", err)
}
-
+
// Create analysis with subtasks
analysis := &tm.SubtaskAnalysis{
ParentTaskID: "test-task-123",
@@ -593,45 +593,45 @@
AssignedTo: "backend",
},
{
- Title: "Subtask 2",
+ Title: "Subtask 2",
AssignedTo: "frontend",
},
},
}
-
+
// Update the parent task
err = service.updateParentTaskAsCompleted(tmpFile.Name(), analysis)
if err != nil {
t.Fatalf("updateParentTaskAsCompleted failed: %v", err)
}
-
+
// Read the updated file
updatedContent, err := os.ReadFile(tmpFile.Name())
if err != nil {
t.Fatalf("Failed to read updated file: %v", err)
}
-
+
updatedString := string(updatedContent)
-
+
// Verify the status was changed to completed
if !strings.Contains(updatedString, "status: completed") {
t.Error("Updated file should contain 'status: completed'")
}
-
+
// Verify completed_at was set (should not be null)
if strings.Contains(updatedString, "completed_at: null") {
t.Error("Updated file should have completed_at timestamp, not null")
}
-
+
// Verify subtask information was added
if !strings.Contains(updatedString, "## Subtasks Created") {
t.Error("Updated file should contain subtasks information")
}
-
+
if !strings.Contains(updatedString, "test-task-123-subtask-1") {
t.Error("Updated file should reference created subtask IDs")
}
-
+
if !strings.Contains(updatedString, "**Total Estimated Hours:** 20") {
t.Error("Updated file should contain total estimated hours")
}
@@ -640,7 +640,7 @@
func TestClose(t *testing.T) {
mockProvider := NewMockLLMProvider([]string{})
service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil, nil)
-
+
err := service.Close()
if err != nil {
t.Errorf("Close should not return error, got: %v", err)
@@ -667,14 +667,14 @@
mockProvider := NewMockLLMProvider([]string{jsonResponse})
service := NewSubtaskService(mockProvider, nil, []string{"backend", "frontend"}, nil, "example", "repo", nil, nil)
-
+
task := &tm.Task{
ID: "benchmark-task",
Title: "Benchmark Task",
Description: "Task for benchmarking",
Priority: tm.PriorityHigh,
}
-
+
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Reset mock provider for each iteration
@@ -684,4 +684,4 @@
b.Fatalf("AnalyzeTaskForSubtasks failed: %v", err)
}
}
-}
\ No newline at end of file
+}
diff --git a/server/tasks/task-1753623184-58260673.md b/server/tasks/task-1753623184-58260673.md
deleted file mode 100644
index d92637b..0000000
--- a/server/tasks/task-1753623184-58260673.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-assignee: backend-engineer
-created_at: "2025-07-27T17:33:04+04:00"
-description: Create a simple test to verify the MVP system works
-id: task-1753623184-58260673
-owner_id: user
-owner_name: user
-priority: high
-status: todo
-title: Test MVP functionality
-updated_at: "2025-07-27T17:38:01+04:00"
----
-
-# Task Description
-
-Create a simple test to verify the MVP system works
-
diff --git a/server/tasks/task-1753636924-a1d4c708.md b/server/tasks/task-1753636924-a1d4c708.md
deleted file mode 100644
index 994b4e9..0000000
--- a/server/tasks/task-1753636924-a1d4c708.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-assignee: ""
-created_at: "2025-07-27T21:22:04+04:00"
-description: This is a test task to verify the MVP is working
-id: task-1753636924-a1d4c708
-owner_id: user
-owner_name: user
-priority: high
-status: todo
-title: Test task for MVP
-updated_at: "2025-07-27T21:22:04+04:00"
----
-
-# Task Description
-
-This is a test task to verify the MVP is working
-