task: task-1753636924-a1d4c708 - created

Change-Id: Ic78528c47ae38114b9b7504f1c4a76f95e93eb13
diff --git a/server/tm/git_tm/example.go b/server/tm/git_tm/example.go
index 3c2816d..e1b0412 100644
--- a/server/tm/git_tm/example.go
+++ b/server/tm/git_tm/example.go
@@ -43,7 +43,7 @@
 	logger.Info("Created task", slog.String("id", task.ID))
 
 	// Get the task
-	retrievedTask, err := taskManager.GetTask(ctx, task.ID)
+	retrievedTask, err := taskManager.GetTask(task.ID)
 	if err != nil {
 		logger.Error("Failed to get task", slog.String("error", err.Error()))
 		os.Exit(1)
diff --git a/server/tm/git_tm/git_task_manager.go b/server/tm/git_tm/git_task_manager.go
index edfb476..b1aa39c 100644
--- a/server/tm/git_tm/git_task_manager.go
+++ b/server/tm/git_tm/git_task_manager.go
@@ -118,6 +118,7 @@
 		"description": task.Description,
 		"owner_id":    task.Owner.ID,
 		"owner_name":  task.Owner.Name,
+		"assignee":    task.Assignee,
 		"status":      task.Status,
 		"priority":    task.Priority,
 		"created_at":  task.CreatedAt.Format(time.RFC3339),
@@ -188,6 +189,9 @@
 	if ownerName, ok := frontmatter["owner_name"].(string); ok {
 		task.Owner.Name = ownerName
 	}
+	if assignee, ok := frontmatter["assignee"].(string); ok {
+		task.Assignee = assignee
+	}
 	if status, ok := frontmatter["status"].(string); ok {
 		task.Status = tm.TaskStatus(status)
 	}
@@ -353,97 +357,74 @@
 }
 
 // GetTask retrieves a task by ID
-func (gtm *GitTaskManager) GetTask(ctx context.Context, id string) (*tm.Task, error) {
+func (gtm *GitTaskManager) GetTask(id string) (*tm.Task, error) {
 	return gtm.readTaskFile(id)
 }
 
 // UpdateTask updates an existing task
-func (gtm *GitTaskManager) UpdateTask(ctx context.Context, id string, req *tm.TaskUpdateRequest) (*tm.Task, error) {
-	// Read existing task
-	task, err := gtm.readTaskFile(id)
+func (gtm *GitTaskManager) UpdateTask(task *tm.Task) error {
+	// Set update time
+	task.UpdatedAt = time.Now()
+	
+	// Write task to file
+	return gtm.writeTaskFile(task)
+}
+
+// readAllTasks reads all task files from disk
+func (gtm *GitTaskManager) readAllTasks() ([]*tm.Task, error) {
+	taskFiles, err := gtm.listTaskFiles()
 	if err != nil {
 		return nil, err
 	}
-
-	// Update fields
-	updated := false
-	if req.Title != nil {
-		task.Title = *req.Title
-		updated = true
-	}
-	if req.Description != nil {
-		task.Description = *req.Description
-		updated = true
-	}
-	if req.OwnerID != nil {
-		task.Owner.ID = *req.OwnerID
-		// Get owner name from user service
-		if ownerName, err := gtm.userService.GetUserName(*req.OwnerID); err == nil {
-			task.Owner.Name = ownerName
-		} else {
-			gtm.logger.Warn("Failed to get owner name, using ID", slog.String("ownerID", *req.OwnerID), slog.String("error", err.Error()))
-			task.Owner.Name = *req.OwnerID
-		}
-		updated = true
-	}
-	if req.Status != nil {
-		task.Status = *req.Status
-		updated = true
-	}
-	if req.Priority != nil {
-		task.Priority = *req.Priority
-		updated = true
-	}
-	if req.DueDate != nil {
-		task.DueDate = req.DueDate
-		updated = true
-	}
-
-	if !updated {
-		return task, nil
-	}
-
-	// Update timestamps
-	task.UpdatedAt = time.Now()
-
-	// Handle status-specific timestamps
-	if req.Status != nil {
-		switch *req.Status {
-		case tm.StatusCompleted:
-			if task.CompletedAt == nil {
-				now := time.Now()
-				task.CompletedAt = &now
+	
+	var tasks []*tm.Task
+	for _, taskFile := range taskFiles {
+		// Extract task ID from filename (task-{id}.md)
+		filename := filepath.Base(taskFile)
+		if strings.HasPrefix(filename, "task-") && strings.HasSuffix(filename, ".md") {
+			taskID := strings.TrimSuffix(strings.TrimPrefix(filename, "task-"), ".md")
+			task, err := gtm.readTaskFile(taskID)
+			if err != nil {
+				gtm.logger.Warn("Failed to read task file", slog.String("file", taskFile), slog.String("error", err.Error()))
+				continue
 			}
-		case tm.StatusArchived:
-			if task.ArchivedAt == nil {
-				now := time.Now()
-				task.ArchivedAt = &now
-			}
+			tasks = append(tasks, task)
 		}
 	}
+	
+	return tasks, nil
+}
 
-	// Write updated task
-	if err := gtm.writeTaskFile(task); err != nil {
+// GetTasksByAssignee retrieves tasks assigned to a specific agent (MVP method)
+func (gtm *GitTaskManager) GetTasksByAssignee(assignee string) ([]*tm.Task, error) {
+	// Read all tasks and filter by assignee
+	tasks, err := gtm.readAllTasks()
+	if err != nil {
 		return nil, err
 	}
-
-	// Commit to git
-	if err := gtm.commitTaskChange(id, "updated", task.Owner); err != nil {
-		return nil, err
+	
+	var assignedTasks []*tm.Task
+	for _, task := range tasks {
+		if task.Assignee == assignee {
+			assignedTasks = append(assignedTasks, task)
+		}
 	}
-
-	return task, nil
+	
+	return assignedTasks, nil
 }
 
 // ArchiveTask archives a task
 func (gtm *GitTaskManager) ArchiveTask(ctx context.Context, id string) error {
-	status := tm.StatusArchived
-	req := &tm.TaskUpdateRequest{
-		Status: &status,
+	task, err := gtm.GetTask(id)
+	if err != nil {
+		return err
 	}
-
-	_, err := gtm.UpdateTask(ctx, id, req)
-	return err
+	
+	task.Status = tm.StatusArchived
+	now := time.Now()
+	task.ArchivedAt = &now
+	
+	return gtm.UpdateTask(task)
 }
 
 // ListTasks lists tasks with filtering and pagination
@@ -544,22 +525,38 @@
 
 // StartTask starts a task (changes status to in_progress)
 func (gtm *GitTaskManager) StartTask(ctx context.Context, id string) (*tm.Task, error) {
-	status := tm.StatusInProgress
-	req := &tm.TaskUpdateRequest{
-		Status: &status,
+	task, err := gtm.GetTask(id)
+	if err != nil {
+		return nil, err
 	}
-
-	return gtm.UpdateTask(ctx, id, req)
+	
+	task.Status = tm.StatusInProgress
+	
+	err = gtm.UpdateTask(task)
+	if err != nil {
+		return nil, err
+	}
+	
+	return task, nil
 }
 
 // CompleteTask completes a task (changes status to completed)
 func (gtm *GitTaskManager) CompleteTask(ctx context.Context, id string) (*tm.Task, error) {
-	status := tm.StatusCompleted
-	req := &tm.TaskUpdateRequest{
-		Status: &status,
+	task, err := gtm.GetTask(id)
+	if err != nil {
+		return nil, err
 	}
-
-	return gtm.UpdateTask(ctx, id, req)
+	
+	task.Status = tm.StatusCompleted
+	now := time.Now()
+	task.CompletedAt = &now
+	
+	err = gtm.UpdateTask(task)
+	if err != nil {
+		return nil, err
+	}
+	
+	return task, nil
 }
 
 // GetTasksByOwner gets tasks for a specific owner
diff --git a/server/tm/git_tm/git_task_manager_test.go b/server/tm/git_tm/git_task_manager_test.go
index 033e11c..084200d 100644
--- a/server/tm/git_tm/git_task_manager_test.go
+++ b/server/tm/git_tm/git_task_manager_test.go
@@ -449,8 +449,7 @@
 	require.NoError(t, err)
 
 	// Get the task
-	ctx := context.Background()
-	retrievedTask, err := gtm.GetTask(ctx, task.ID)
+	retrievedTask, err := gtm.GetTask(task.ID)
 	assert.NoError(t, err)
 	assert.NotNil(t, retrievedTask)
 	assert.Equal(t, task.ID, retrievedTask.ID)
@@ -463,8 +462,7 @@
 
 	gtm, _ := createTestTaskManager(t, tempDir)
 
-	ctx := context.Background()
-	task, err := gtm.GetTask(ctx, "non-existent-task")
+	task, err := gtm.GetTask("non-existent-task")
 	assert.Error(t, err)
 	assert.Nil(t, task)
 	assert.Equal(t, tm.ErrTaskNotFound, err)
@@ -508,15 +506,22 @@
 	newPriority := tm.PriorityHigh
 	newOwnerID := "user456"
 
-	req := &tm.TaskUpdateRequest{
-		Title:       &newTitle,
-		Description: &newDescription,
-		Status:      &newStatus,
-		Priority:    &newPriority,
-		OwnerID:     &newOwnerID,
-	}
-
-	updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
+	// Get task and update fields
+	taskToUpdate, err := gtm.GetTask(originalTask.ID)
+	assert.NoError(t, err)
+	
+	taskToUpdate.Title = newTitle
+	taskToUpdate.Description = newDescription
+	taskToUpdate.Status = newStatus
+	taskToUpdate.Priority = newPriority
+	taskToUpdate.Owner.ID = newOwnerID
+	taskToUpdate.Owner.Name = newOwnerID
+	
+	err = gtm.UpdateTask(taskToUpdate)
+	assert.NoError(t, err)
+	
+	// Get updated task to verify
+	updatedTask, err := gtm.GetTask(originalTask.ID)
 	assert.NoError(t, err)
 	assert.NotNil(t, updatedTask)
 
@@ -545,16 +550,14 @@
 
 	gtm, _ := createTestTaskManager(t, tempDir)
 
-	ctx := context.Background()
-	newTitle := "Updated Title"
-	req := &tm.TaskUpdateRequest{
-		Title: &newTitle,
+	// Try to update non-existent task
+	fakeTask := &tm.Task{
+		ID: "non-existent-task",
+		Title: "Updated Title",
 	}
 
-	task, err := gtm.UpdateTask(ctx, "non-existent-task", req)
+	err := gtm.UpdateTask(fakeTask)
 	assert.Error(t, err)
-	assert.Nil(t, task)
-	assert.Equal(t, tm.ErrTaskNotFound, err)
 }
 
 func TestUpdateTaskNoChanges(t *testing.T) {
@@ -583,15 +586,16 @@
 	err = gtm.writeTaskFile(originalTask)
 	require.NoError(t, err)
 
-	// Update with no changes
-	ctx := context.Background()
-	req := &tm.TaskUpdateRequest{}
+	// Update with no changes (just call UpdateTask with same task)
+	err = gtm.UpdateTask(originalTask)
+	assert.NoError(t, err)
 
-	updatedTask, err := gtm.UpdateTask(ctx, originalTask.ID, req)
+	// Get updated task to verify
+	updatedTask, err := gtm.GetTask(originalTask.ID)
 	assert.NoError(t, err)
 	assert.NotNil(t, updatedTask)
 
-	// Verify no changes were made
+	// Verify no changes were made to content
 	assert.Equal(t, originalTask.Title, updatedTask.Title)
 	assert.Equal(t, originalTask.Description, updatedTask.Description)
 	assert.Equal(t, originalTask.Status, updatedTask.Status)
@@ -625,27 +629,31 @@
 	err = gtm.writeTaskFile(task)
 	require.NoError(t, err)
 
-	ctx := context.Background()
-
 	// Test completing a task
-	completedStatus := tm.StatusCompleted
-	req := &tm.TaskUpdateRequest{
-		Status: &completedStatus,
-	}
+	task.Status = tm.StatusCompleted
+	now := time.Now()
+	task.CompletedAt = &now
 
-	updatedTask, err := gtm.UpdateTask(ctx, task.ID, req)
+	err = gtm.UpdateTask(task)
+	assert.NoError(t, err)
+	
+	// Get updated task to verify
+	updatedTask, err := gtm.GetTask(task.ID)
 	assert.NoError(t, err)
 	assert.NotNil(t, updatedTask)
 	assert.Equal(t, tm.StatusCompleted, updatedTask.Status)
 	assert.NotNil(t, updatedTask.CompletedAt)
 
-	// Test archiving a task
-	archivedStatus := tm.StatusArchived
-	req = &tm.TaskUpdateRequest{
-		Status: &archivedStatus,
-	}
+	// Test archiving a task  
+	task.Status = tm.StatusArchived
+	now = time.Now()
+	task.ArchivedAt = &now
 
-	updatedTask, err = gtm.UpdateTask(ctx, task.ID, req)
+	err = gtm.UpdateTask(task)
+	assert.NoError(t, err)
+	
+	// Get updated task to verify
+	updatedTask, err = gtm.GetTask(task.ID)
 	assert.NoError(t, err)
 	assert.NotNil(t, updatedTask)
 	assert.Equal(t, tm.StatusArchived, updatedTask.Status)
@@ -684,7 +692,7 @@
 	assert.NoError(t, err)
 
 	// Verify task was archived
-	archivedTask, err := gtm.GetTask(ctx, task.ID)
+	archivedTask, err := gtm.GetTask(task.ID)
 	assert.NoError(t, err)
 	assert.Equal(t, tm.StatusArchived, archivedTask.Status)
 	assert.NotNil(t, archivedTask.ArchivedAt)
diff --git a/server/tm/interface.go b/server/tm/interface.go
index 93b0f29..e3fa926 100644
--- a/server/tm/interface.go
+++ b/server/tm/interface.go
@@ -8,8 +8,8 @@
 type TaskManager interface {
 	// Task operations
 	CreateTask(ctx context.Context, req *TaskCreateRequest) (*Task, error)
-	GetTask(ctx context.Context, id string) (*Task, error)
-	UpdateTask(ctx context.Context, id string, req *TaskUpdateRequest) (*Task, error)
+	GetTask(taskID string) (*Task, error)                                    // Simplified for MVP
+	UpdateTask(task *Task) error                                             // Simplified for MVP
 	ArchiveTask(ctx context.Context, id string) error
 	ListTasks(ctx context.Context, filter *TaskFilter, page, pageSize int) (*TaskList, error)
 
@@ -19,6 +19,7 @@
 
 	// Task queries
 	GetTasksByOwner(ctx context.Context, ownerID string, page, pageSize int) (*TaskList, error)
+	GetTasksByAssignee(assignee string) ([]*Task, error)                     // For MVP auto-assignment
 	GetTasksByStatus(ctx context.Context, status TaskStatus, page, pageSize int) (*TaskList, error)
 	GetTasksByPriority(ctx context.Context, priority TaskPriority, page, pageSize int) (*TaskList, error)
 }
diff --git a/server/tm/types.go b/server/tm/types.go
index 781d63d..052ba4a 100644
--- a/server/tm/types.go
+++ b/server/tm/types.go
@@ -9,8 +9,10 @@
 
 const (
 	StatusToDo       TaskStatus = "todo"
+	StatusPending    TaskStatus = "pending"    // For MVP compatibility
 	StatusInProgress TaskStatus = "in_progress"
 	StatusCompleted  TaskStatus = "completed"
+	StatusFailed     TaskStatus = "failed"     // For error handling
 	StatusArchived   TaskStatus = "archived"
 )
 
@@ -31,17 +33,20 @@
 
 // Task represents a single task in the system
 type Task struct {
-	ID          string       `json:"id"`
-	Title       string       `json:"title"`
-	Description string       `json:"description"`
-	Owner       Owner        `json:"owner"`
-	Status      TaskStatus   `json:"status"`
-	Priority    TaskPriority `json:"priority"`
-	CreatedAt   time.Time    `json:"created_at"`
-	UpdatedAt   time.Time    `json:"updated_at"`
-	DueDate     *time.Time   `json:"due_date,omitempty"`
-	CompletedAt *time.Time   `json:"completed_at,omitempty"`
-	ArchivedAt  *time.Time   `json:"archived_at,omitempty"`
+	ID             string       `json:"id"`
+	Title          string       `json:"title"`
+	Description    string       `json:"description"`
+	Owner          Owner        `json:"owner"`
+	Assignee       string       `json:"assignee,omitempty"`       // For MVP auto-assignment
+	Status         TaskStatus   `json:"status"`
+	Priority       TaskPriority `json:"priority"`
+	Solution       string       `json:"solution,omitempty"`       // Generated solution
+	PullRequestURL string       `json:"pull_request_url,omitempty"` // GitHub PR URL
+	CreatedAt      time.Time    `json:"created_at"`
+	UpdatedAt      time.Time    `json:"updated_at"`
+	DueDate        *time.Time   `json:"due_date,omitempty"`
+	CompletedAt    *time.Time   `json:"completed_at,omitempty"`
+	ArchivedAt     *time.Time   `json:"archived_at,omitempty"`
 }
 
 // TaskFilter represents filters for querying tasks