Add subtasks to the PR

Change-Id: I386be06949a54dee518e9b6a23344d76099b88ba
diff --git a/server/config.yaml b/server/config.yaml
index 7baf0f9..5a5b6dd 100644
--- a/server/config.yaml
+++ b/server/config.yaml
@@ -8,7 +8,7 @@
 
 github:
   token: "${GITHUB_TOKEN}"
-  owner: "shota"  # Replace with your GitHub username
+  owner: "iomodo"  # Replace with your GitHub username
   repo: "staff"   # Replace with your repository name
 
 git:
diff --git a/server/git/github.go b/server/git/github.go
index 8f15c68..63424f1 100644
--- a/server/git/github.go
+++ b/server/git/github.go
@@ -123,7 +123,12 @@
 		return nil, fmt.Errorf("failed to marshal request body: %w", err)
 	}
 
+	// Debug logging for request data
+	fmt.Printf("DEBUG: Creating PR with data: %s\n", string(jsonBody))
+
 	url := fmt.Sprintf("%s/repos/%s/%s/pulls", g.config.BaseURL, g.owner, g.repo)
+	fmt.Printf("DEBUG: POST URL: %s\n", url)
+	
 	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
 	if err != nil {
 		return nil, fmt.Errorf("failed to create request: %w", err)
@@ -140,7 +145,10 @@
 	defer resp.Body.Close()
 
 	if resp.StatusCode != http.StatusCreated {
-		return nil, fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+		// Read the error response body for detailed error information
+		var errorBody bytes.Buffer
+		_, _ = errorBody.ReadFrom(resp.Body)
+		return nil, fmt.Errorf("GitHub API error: %d - %s", resp.StatusCode, errorBody.String())
 	}
 
 	var githubPR githubPullRequest
diff --git a/server/subtasks/service.go b/server/subtasks/service.go
index 5f99f74..c1a1bc9 100644
--- a/server/subtasks/service.go
+++ b/server/subtasks/service.go
@@ -421,6 +421,7 @@
 
 	// Generate branch name for subtask proposal
 	branchName := fmt.Sprintf("subtasks/%s-proposal", analysis.ParentTaskID)
+	log.Printf("Creating subtask PR with branch: %s", branchName)
 
 	// Create Git branch and commit subtask proposal
 	if err := s.createSubtaskBranch(ctx, analysis, branchName); err != nil {
@@ -430,17 +431,31 @@
 	// Generate PR content
 	prContent := s.generateSubtaskPRContent(analysis)
 	title := fmt.Sprintf("Subtask Proposal: %s", analysis.ParentTaskID)
+	
+	// Validate PR content
+	if title == "" {
+		return "", fmt.Errorf("PR title cannot be empty")
+	}
+	if prContent == "" {
+		return "", fmt.Errorf("PR description cannot be empty")
+	}
+
+	// Determine base branch (try main first, fallback to master)
+	baseBranch := s.determineBaseBranch(ctx)
+	log.Printf("Using base branch: %s", baseBranch)
 
 	// Create the pull request
 	options := git.PullRequestOptions{
 		Title:       title,
 		Description: prContent,
 		HeadBranch:  branchName,
-		BaseBranch:  "main",
+		BaseBranch:  baseBranch,
 		Labels:      []string{"subtasks", "proposal", "ai-generated"},
 		Draft:       false,
 	}
 
+	log.Printf("Creating PR with options: title=%s, head=%s, base=%s", options.Title, options.HeadBranch, options.BaseBranch)
+
 	pr, err := s.prProvider.CreatePullRequest(ctx, options)
 	if err != nil {
 		return "", fmt.Errorf("failed to create PR: %w", err)
@@ -452,15 +467,220 @@
 	return prURL, nil
 }
 
+// determineBaseBranch determines the correct base branch (main or master)
+func (s *SubtaskService) determineBaseBranch(ctx context.Context) string {
+	if s.cloneManager == nil {
+		return "main" // default fallback
+	}
+
+	// Get clone path to check branches
+	clonePath, err := s.cloneManager.GetAgentClonePath("subtask-service")
+	if err != nil {
+		log.Printf("Warning: failed to get clone path for base branch detection: %v", err)
+		return "main"
+	}
+
+	// Check if main branch exists
+	gitCmd := func(args ...string) *exec.Cmd {
+		return exec.CommandContext(ctx, "git", append([]string{"-C", clonePath}, args...)...)
+	}
+
+	// Try to checkout main branch
+	cmd := gitCmd("show-ref", "refs/remotes/origin/main")
+	if err := cmd.Run(); err == nil {
+		return "main"
+	}
+
+	// Try to checkout master branch
+	cmd = gitCmd("show-ref", "refs/remotes/origin/master")
+	if err := cmd.Run(); err == nil {
+		return "master"
+	}
+
+	// Default to main if neither can be detected
+	log.Printf("Warning: Could not determine base branch, defaulting to 'main'")
+	return "main"
+}
+
+// generateSubtaskFile creates the content for an individual subtask file
+func (s *SubtaskService) generateSubtaskFile(subtask tm.SubtaskProposal, taskID, parentTaskID string) string {
+	var content strings.Builder
+	
+	// Generate YAML frontmatter
+	content.WriteString("---\n")
+	content.WriteString(fmt.Sprintf("id: %s\n", taskID))
+	content.WriteString(fmt.Sprintf("title: %s\n", subtask.Title))
+	content.WriteString(fmt.Sprintf("description: %s\n", subtask.Description))
+	content.WriteString(fmt.Sprintf("assignee: %s\n", subtask.AssignedTo))
+	content.WriteString(fmt.Sprintf("owner_id: %s\n", subtask.AssignedTo))
+	content.WriteString(fmt.Sprintf("owner_name: %s\n", subtask.AssignedTo))
+	content.WriteString(fmt.Sprintf("status: todo\n"))
+	content.WriteString(fmt.Sprintf("priority: %s\n", strings.ToLower(string(subtask.Priority))))
+	content.WriteString(fmt.Sprintf("parent_task_id: %s\n", parentTaskID))
+	content.WriteString(fmt.Sprintf("estimated_hours: %d\n", subtask.EstimatedHours))
+	content.WriteString(fmt.Sprintf("created_at: %s\n", time.Now().Format(time.RFC3339)))
+	content.WriteString(fmt.Sprintf("updated_at: %s\n", time.Now().Format(time.RFC3339)))
+	content.WriteString("completed_at: null\n")
+	content.WriteString("archived_at: null\n")
+	
+	// Add dependencies if any
+	if len(subtask.Dependencies) > 0 {
+		content.WriteString("dependencies:\n")
+		for _, dep := range subtask.Dependencies {
+			// Convert dependency index to actual subtask ID
+			if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
+				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
+				content.WriteString(fmt.Sprintf("  - %s\n", depTaskID))
+			}
+		}
+	}
+	
+	// Add required skills if any
+	if len(subtask.RequiredSkills) > 0 {
+		content.WriteString("required_skills:\n")
+		for _, skill := range subtask.RequiredSkills {
+			content.WriteString(fmt.Sprintf("  - %s\n", skill))
+		}
+	}
+	
+	content.WriteString("---\n\n")
+	
+	// Add markdown content
+	content.WriteString("# Task Description\n\n")
+	content.WriteString(fmt.Sprintf("%s\n\n", subtask.Description))
+	
+	if subtask.EstimatedHours > 0 {
+		content.WriteString("## Estimated Effort\n\n")
+		content.WriteString(fmt.Sprintf("**Estimated Hours:** %d\n\n", subtask.EstimatedHours))
+	}
+	
+	if len(subtask.RequiredSkills) > 0 {
+		content.WriteString("## Required Skills\n\n")
+		for _, skill := range subtask.RequiredSkills {
+			content.WriteString(fmt.Sprintf("- %s\n", skill))
+		}
+		content.WriteString("\n")
+	}
+	
+	if len(subtask.Dependencies) > 0 {
+		content.WriteString("## Dependencies\n\n")
+		content.WriteString("This task depends on the completion of:\n\n")
+		for _, dep := range subtask.Dependencies {
+			if depIndex := s.parseDependencyIndex(dep); depIndex >= 0 {
+				depTaskID := fmt.Sprintf("%s-subtask-%d", parentTaskID, depIndex+1)
+				content.WriteString(fmt.Sprintf("- %s\n", depTaskID))
+			}
+		}
+		content.WriteString("\n")
+	}
+	
+	content.WriteString("## Notes\n\n")
+	content.WriteString(fmt.Sprintf("This subtask was generated from parent task: %s\n", parentTaskID))
+	content.WriteString("Generated by Staff AI Agent System\n\n")
+	
+	return content.String()
+}
+
+// updateParentTaskAsCompleted updates the parent task file to mark it as completed
+func (s *SubtaskService) updateParentTaskAsCompleted(taskFilePath string, analysis *tm.SubtaskAnalysis) error {
+	// Read the existing parent task file
+	content, err := os.ReadFile(taskFilePath)
+	if err != nil {
+		return fmt.Errorf("failed to read parent task file: %w", err)
+	}
+
+	taskContent := string(content)
+	
+	// Find the YAML frontmatter boundaries
+	lines := strings.Split(taskContent, "\n")
+	var frontmatterStart, frontmatterEnd int = -1, -1
+	
+	for i, line := range lines {
+		if line == "---" {
+			if frontmatterStart == -1 {
+				frontmatterStart = i
+			} else {
+				frontmatterEnd = i
+				break
+			}
+		}
+	}
+	
+	if frontmatterStart == -1 || frontmatterEnd == -1 {
+		return fmt.Errorf("invalid task file format: missing YAML frontmatter")
+	}
+	
+	// Update the frontmatter
+	now := time.Now().Format(time.RFC3339)
+	var updatedLines []string
+	
+	// Add lines before frontmatter
+	updatedLines = append(updatedLines, lines[:frontmatterStart+1]...)
+	
+	// Process frontmatter lines
+	for i := frontmatterStart + 1; i < frontmatterEnd; i++ {
+		line := lines[i]
+		if strings.HasPrefix(line, "status:") {
+			updatedLines = append(updatedLines, "status: completed")
+		} else if strings.HasPrefix(line, "updated_at:") {
+			updatedLines = append(updatedLines, fmt.Sprintf("updated_at: %s", now))
+		} else if strings.HasPrefix(line, "completed_at:") {
+			updatedLines = append(updatedLines, fmt.Sprintf("completed_at: %s", now))
+		} else {
+			updatedLines = append(updatedLines, line)
+		}
+	}
+	
+	// Add closing frontmatter and rest of content
+	updatedLines = append(updatedLines, lines[frontmatterEnd:]...)
+	
+	// Add subtask information to the task description
+	if frontmatterEnd+1 < len(lines) {
+		// Add subtask information
+		subtaskInfo := fmt.Sprintf("\n\n## Subtasks Created\n\nThis task has been broken down into %d subtasks:\n\n", len(analysis.Subtasks))
+		for i, subtask := range analysis.Subtasks {
+			subtaskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+			subtaskInfo += fmt.Sprintf("- **%s**: %s (assigned to %s)\n", subtaskID, subtask.Title, subtask.AssignedTo)
+		}
+		subtaskInfo += fmt.Sprintf("\n**Total Estimated Hours:** %d\n", analysis.EstimatedTotalHours)
+		subtaskInfo += fmt.Sprintf("**Completed:** %s - Task broken down into actionable subtasks\n", now)
+		
+		// Insert subtask info before any existing body content
+		updatedContent := strings.Join(updatedLines[:len(updatedLines)], "\n") + subtaskInfo
+		
+		// Write the updated content back to the file
+		if err := os.WriteFile(taskFilePath, []byte(updatedContent), 0644); err != nil {
+			return fmt.Errorf("failed to write updated parent task file: %w", err)
+		}
+	}
+	
+	log.Printf("Updated parent task %s to completed status", analysis.ParentTaskID)
+	return nil
+}
+
 // generateSubtaskPRContent creates markdown content for the subtask proposal PR
 func (s *SubtaskService) generateSubtaskPRContent(analysis *tm.SubtaskAnalysis) string {
 	var content strings.Builder
 	
-	content.WriteString(fmt.Sprintf("# Subtask Proposal for Task %s\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("# Subtasks Created for Task %s\n\n", analysis.ParentTaskID))
+	content.WriteString(fmt.Sprintf("This PR creates **%d individual task files** in `/operations/tasks/` ready for agent assignment.\n\n", len(analysis.Subtasks)))
+	content.WriteString(fmt.Sprintf("✅ **Parent task `%s` has been marked as completed** - the complex task has been successfully broken down into actionable subtasks.\n\n", analysis.ParentTaskID))
 	content.WriteString(fmt.Sprintf("## Analysis Summary\n%s\n\n", analysis.AnalysisSummary))
 	content.WriteString(fmt.Sprintf("## Recommended Approach\n%s\n\n", analysis.RecommendedApproach))
 	content.WriteString(fmt.Sprintf("**Estimated Total Hours:** %d\n\n", analysis.EstimatedTotalHours))
 	
+	// List the created task files
+	content.WriteString("## Created Task Files\n\n")
+	for i, subtask := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		content.WriteString(fmt.Sprintf("### %d. `%s.md`\n", i+1, taskID))
+		content.WriteString(fmt.Sprintf("- **Title:** %s\n", subtask.Title))
+		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
+		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
+		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
+		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
+	}
+	
 	if analysis.RiskAssessment != "" {
 		content.WriteString(fmt.Sprintf("## Risk Assessment\n%s\n\n", analysis.RiskAssessment))
 	}
@@ -524,35 +744,76 @@
 		log.Printf("Warning: failed to pull latest changes: %v", err)
 	}
 
+	// Delete branch if it exists (cleanup from previous attempts)
+	cmd = gitCmd("branch", "-D", branchName)
+	_ = cmd.Run() // Ignore error if branch doesn't exist
+
+	// Also delete remote tracking branch if it exists
+	cmd = gitCmd("push", "origin", "--delete", branchName)
+	_ = cmd.Run() // Ignore error if branch doesn't exist
+
 	// Create and checkout new branch
 	cmd = gitCmd("checkout", "-b", branchName)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("failed to create branch: %w", err)
 	}
 
-	// Create subtask proposal file
-	proposalDir := filepath.Join(clonePath, "tasks", "subtasks")
-	if err := os.MkdirAll(proposalDir, 0755); err != nil {
-		return fmt.Errorf("failed to create proposal directory: %w", err)
+	// Create individual task files for each subtask
+	tasksDir := filepath.Join(clonePath, "operations", "tasks")
+	if err := os.MkdirAll(tasksDir, 0755); err != nil {
+		return fmt.Errorf("failed to create tasks directory: %w", err)
 	}
 
-	proposalFile := filepath.Join(proposalDir, fmt.Sprintf("%s-proposal.md", analysis.ParentTaskID))
-	proposalContent := s.generateSubtaskProposalFile(analysis)
+	var stagedFiles []string
 
-	if err := os.WriteFile(proposalFile, []byte(proposalContent), 0644); err != nil {
-		return fmt.Errorf("failed to write proposal file: %w", err)
+	// Update parent task to mark as completed
+	parentTaskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", analysis.ParentTaskID))
+	if err := s.updateParentTaskAsCompleted(parentTaskFile, analysis); err != nil {
+		return fmt.Errorf("failed to update parent task: %w", err)
+	}
+	
+	// Track parent task file for staging
+	parentRelativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", analysis.ParentTaskID))
+	stagedFiles = append(stagedFiles, parentRelativeFile)
+	log.Printf("Updated parent task file: %s", parentRelativeFile)
+
+	// Create a file for each subtask
+	for i, subtask := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		taskFile := filepath.Join(tasksDir, fmt.Sprintf("%s.md", taskID))
+		taskContent := s.generateSubtaskFile(subtask, taskID, analysis.ParentTaskID)
+
+		if err := os.WriteFile(taskFile, []byte(taskContent), 0644); err != nil {
+			return fmt.Errorf("failed to write subtask file %s: %w", taskID, err)
+		}
+
+		// Track file for staging
+		relativeFile := filepath.Join("operations", "tasks", fmt.Sprintf("%s.md", taskID))
+		stagedFiles = append(stagedFiles, relativeFile)
+		log.Printf("Created subtask file: %s", relativeFile)
 	}
 
-	// Stage the file
-	relativeFile := filepath.Join("tasks", "subtasks", fmt.Sprintf("%s-proposal.md", analysis.ParentTaskID))
-	cmd = gitCmd("add", relativeFile)
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("failed to stage files: %w", err)
+	// Stage all subtask files
+	for _, file := range stagedFiles {
+		cmd = gitCmd("add", file)
+		if err := cmd.Run(); err != nil {
+			return fmt.Errorf("failed to stage file %s: %w", file, err)
+		}
 	}
 
 	// Commit changes
-	commitMsg := fmt.Sprintf("Subtask proposal for task %s\n\nGenerated by Staff AI Agent System\nProposed %d subtasks with %d new agents", 
-		analysis.ParentTaskID, len(analysis.Subtasks), len(analysis.AgentCreations))
+	commitMsg := fmt.Sprintf("Create %d subtasks for task %s and mark parent as completed\n\nGenerated by Staff AI Agent System\n\nFiles modified:\n- %s.md (marked as completed)\n\nCreated individual task files:\n", 
+		len(analysis.Subtasks), analysis.ParentTaskID, analysis.ParentTaskID)
+	
+	// Add list of created files to commit message
+	for i := range analysis.Subtasks {
+		taskID := fmt.Sprintf("%s-subtask-%d", analysis.ParentTaskID, i+1)
+		commitMsg += fmt.Sprintf("- %s.md\n", taskID)
+	}
+	
+	if len(analysis.AgentCreations) > 0 {
+		commitMsg += fmt.Sprintf("\nProposed %d new agents for specialized skills", len(analysis.AgentCreations))
+	}
 	cmd = gitCmd("commit", "-m", commitMsg)
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("failed to commit: %w", err)
@@ -568,51 +829,6 @@
 	return nil
 }
 
-// generateSubtaskProposalFile creates the content for the subtask proposal file
-func (s *SubtaskService) generateSubtaskProposalFile(analysis *tm.SubtaskAnalysis) string {
-	var content strings.Builder
-	
-	content.WriteString(fmt.Sprintf("# Subtask Proposal for Task %s\n\n", analysis.ParentTaskID))
-	content.WriteString(fmt.Sprintf("**Generated:** %s\n\n", time.Now().Format(time.RFC3339)))
-	content.WriteString(fmt.Sprintf("## Analysis Summary\n%s\n\n", analysis.AnalysisSummary))
-
-	if len(analysis.AgentCreations) > 0 {
-		content.WriteString("## Proposed New Agents\n\n")
-		for i, agent := range analysis.AgentCreations {
-			content.WriteString(fmt.Sprintf("### %d. %s Agent\n", i+1, strings.Title(agent.Role)))
-			content.WriteString(fmt.Sprintf("- **Skills:** %s\n", strings.Join(agent.Skills, ", ")))
-			content.WriteString(fmt.Sprintf("- **Description:** %s\n", agent.Description))
-			content.WriteString(fmt.Sprintf("- **Justification:** %s\n\n", agent.Justification))
-		}
-	}
-
-	content.WriteString("## Proposed Subtasks\n\n")
-	for i, subtask := range analysis.Subtasks {
-		content.WriteString(fmt.Sprintf("### %d. %s\n", i+1, subtask.Title))
-		content.WriteString(fmt.Sprintf("- **Assigned to:** %s\n", subtask.AssignedTo))
-		content.WriteString(fmt.Sprintf("- **Priority:** %s\n", subtask.Priority))
-		content.WriteString(fmt.Sprintf("- **Estimated Hours:** %d\n", subtask.EstimatedHours))
-		if len(subtask.RequiredSkills) > 0 {
-			content.WriteString(fmt.Sprintf("- **Required Skills:** %s\n", strings.Join(subtask.RequiredSkills, ", ")))
-		}
-		if len(subtask.Dependencies) > 0 {
-			content.WriteString(fmt.Sprintf("- **Dependencies:** %s\n", strings.Join(subtask.Dependencies, ", ")))
-		}
-		content.WriteString(fmt.Sprintf("- **Description:** %s\n\n", subtask.Description))
-	}
-
-	content.WriteString(fmt.Sprintf("## Recommended Approach\n%s\n\n", analysis.RecommendedApproach))
-	content.WriteString(fmt.Sprintf("**Estimated Total Hours:** %d\n\n", analysis.EstimatedTotalHours))
-
-	if analysis.RiskAssessment != "" {
-		content.WriteString(fmt.Sprintf("## Risk Assessment\n%s\n\n", analysis.RiskAssessment))
-	}
-
-	content.WriteString("---\n\n")
-	content.WriteString("*This proposal was generated by the Staff AI Agent System. Review and approve to proceed with subtask creation.*\n")
-	
-	return content.String()
-}
 
 // Close cleans up the service
 func (s *SubtaskService) Close() error {
diff --git a/server/subtasks/service_test.go b/server/subtasks/service_test.go
index 158bcdc..0a8e473 100644
--- a/server/subtasks/service_test.go
+++ b/server/subtasks/service_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"context"
+	"os"
 	"strings"
 	"testing"
 	"time"
@@ -482,6 +483,160 @@
 	}
 }
 
+func TestGenerateSubtaskFile(t *testing.T) {
+	mockProvider := NewMockLLMProvider([]string{})
+	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil)
+	
+	subtask := tm.SubtaskProposal{
+		Title:          "Implement API endpoints",
+		Description:    "Create REST API endpoints for user management",
+		Priority:       tm.PriorityHigh,
+		AssignedTo:     "backend",
+		EstimatedHours: 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")
+	}
+	if !strings.Contains(content, "title: Implement API endpoints") {
+		t.Error("Generated file should contain task title in frontmatter")
+	}
+	if !strings.Contains(content, "assignee: backend") {
+		t.Error("Generated file should contain assignee in frontmatter")
+	}
+	if !strings.Contains(content, "status: todo") {
+		t.Error("Generated file should have 'todo' status")
+	}
+	if !strings.Contains(content, "priority: high") {
+		t.Error("Generated file should contain priority in frontmatter")
+	}
+	if !strings.Contains(content, "parent_task_id: parent-task") {
+		t.Error("Generated file should contain parent task ID")
+	}
+	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")
+	}
+	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")
+	}
+	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")
+	}
+	if !strings.Contains(content, "Create REST API endpoints for user management") {
+		t.Error("Generated file should contain task description in body")
+	}
+}
+
+func TestUpdateParentTaskAsCompleted(t *testing.T) {
+	mockProvider := NewMockLLMProvider([]string{})
+	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil)
+	
+	// Create a temporary task file for testing
+	taskContent := `---
+id: test-task-123
+title: Test Task
+description: A test task for validation
+assignee: backend
+status: todo
+priority: high
+created_at: 2024-01-01T10:00:00Z
+updated_at: 2024-01-01T10:00:00Z
+completed_at: null
+---
+
+# Task Description
+
+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",
+		EstimatedTotalHours: 20,
+		Subtasks: []tm.SubtaskProposal{
+			{
+				Title:      "Subtask 1",
+				AssignedTo: "backend",
+			},
+			{
+				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")
+	}
+}
+
 func TestClose(t *testing.T) {
 	mockProvider := NewMockLLMProvider([]string{})
 	service := NewSubtaskService(mockProvider, nil, []string{"backend"}, nil, "example", "repo", nil)