Source code for src.integrations.documentation_tasks

"""
Documentation Task Generation for Marcus.

Adds a final documentation task to projects that:
1. Reviews all logged decisions
2. Creates comprehensive README.md
3. Verifies all instructions work
"""

import logging
import uuid
from datetime import datetime, timezone
from typing import List, Optional

from src.core.models import Priority, Task, TaskStatus

logger = logging.getLogger(__name__)


[docs] class DocumentationTaskGenerator: """Generates documentation tasks for projects."""
[docs] @staticmethod def create_documentation_task( existing_tasks: List[Task], project_name: str = "Project" ) -> Optional[Task]: """ Create a final documentation task. Depends on all major implementation tasks. Parameters ---------- existing_tasks : List[Task] List of all project tasks project_name : str Name of the project Returns ------- Optional[Task] Documentation task or None if no implementation tasks exist """ # Find all implementation, testing, and deployment tasks using # actual label format # Labels are generated with prefixes like "component:backend", # "type:feature", etc. implementation_tasks = [ t for t in existing_tasks if any( label in t.labels for label in [ "type:feature", # Implementation tasks "component:backend", "component:frontend", "component:api", "component:database", "component:authentication", "component:ecommerce", ] ) and t.priority in [Priority.HIGH, Priority.URGENT, Priority.MEDIUM] ] test_tasks = [ t for t in existing_tasks if any(label in t.labels for label in ["type:testing"]) ] deploy_tasks = [ t for t in existing_tasks if any( label in t.labels for label in ["type:deployment", "component:deployment"] ) ] # If no implementation tasks, don't add documentation if not implementation_tasks: logger.info("No implementation tasks found, skipping documentation task") return None # README documentation depends on ALL non-documentation tasks # This ensures it's only available when the entire project is # complete. Integration verification tasks ARE dependencies — # docs should wait for integration to finish. dependencies = [t.id for t in existing_tasks if "documentation" not in t.labels] # CRITICAL VALIDATION: README documentation must depend on implementation tasks # If dependencies is empty but implementation tasks exist, this is a BUG if len(dependencies) == 0 and len(implementation_tasks) > 0: error_msg = ( f"CRITICAL: README documentation task has ZERO dependencies but " f"{len(implementation_tasks)} implementation tasks exist. " f"This means create_documentation_task() was called BEFORE " f"implementation tasks were created. " f"FIX: Call this function AFTER all other tasks are created." ) logger.error(error_msg) raise ValueError(error_msg) # Log dependency count for debugging logger.info( f"README documentation task will depend on {len(dependencies)} tasks" ) # Create the documentation task doc_task = Task( id=f"doc_final_{uuid.uuid4().hex[:8]}", name=f"Create {project_name} README documentation", description=( DocumentationTaskGenerator._generate_documentation_description( has_tests=bool(test_tasks), has_deployment=bool(deploy_tasks), ) ), status=TaskStatus.TODO, priority=Priority.HIGH, labels=["documentation", "final", "verification"], dependencies=dependencies, estimated_hours=4.0, # Adjust based on project size created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), assigned_to=None, due_date=None, ) # Note: acceptance_criteria and subtasks would be stored in # metadata if needed. These are implicit in the task description # above return doc_task
@staticmethod def _generate_documentation_description( has_tests: bool = True, has_deployment: bool = False ) -> str: """Generate detailed description for documentation task.""" base_description = """Create comprehensive README.md documentation by: ⚠️ **IMPORTANT**: Work in the current directory (./) where Claude is running. Create all files in the current working directory, NOT in the Marcus installation directory. 1. **Gather Information**: - Review all logged decisions using `get_task_context` for each completed task - Examine the final codebase structure - Identify key architectural decisions and implementation details 2. **Document Core Sections**: - **How It Works**: System architecture, component interactions, data flow - **How to Run It**: Prerequisites, setup steps, configuration, startup commands - **How to Test It**: Test commands, expected output, coverage reports """ if has_deployment: base_description += ( """ - **How to Deploy It**: Production setup, """ """environment variables, deployment steps """ ) base_description += """3. **Verify Everything**: - Test each command in the documentation - Ensure setup steps work from a clean environment - Verify the application runs as documented - Confirm tests pass as described 4. **Log the Artifact**: ⚠️ **CRITICAL**: After creating or updating README.md, you MUST log it as an artifact using: ``` log_artifact( task_id="<current_task_id>", filename="README.md", content="<full README content>", artifact_type="documentation", project_root="<project_root>", description="Project README with setup, run, and test instructions" ) ``` This is required even if you're updating an existing README.md, not creating it from scratch. 5. **Log Test Verification Results**: ⚠️ **CRITICAL**: After verifying tests work (step 3), you MUST log the test execution results: ``` log_artifact( task_id="<current_task_id>", filename="verification_results.json", content="<json with test results: command, exit_code, pass/fail counts, output>", artifact_type="verification-results", project_root="<project_root>", description="Test verification results from README validation" ) ``` The JSON should include: ```json { "test_command": "pytest tests/", "exit_code": 0, "passed": 45, "failed": 0, "skipped": 2, "execution_time": "2.3s", "raw_output": "===== test session starts =====" } ``` 6. **Format for Success Measurement**: - Use clear markdown formatting - Include specific commands and expected outputs - Document any prerequisites or dependencies - Include troubleshooting for common issues The documentation should be written so someone unfamiliar with the project can successfully set up, run, and test the application by following the instructions.""" return base_description
[docs] @staticmethod def should_add_documentation_task( project_description: str, task_count: int ) -> bool: """ Determine if a documentation task should be added. Parameters ---------- project_description : str Natural language description task_count : int Number of tasks in project Returns ------- bool True if documentation should be added """ # Skip for prototypes/experiments skip_keywords = [ "poc", "proof of concept", "demo", "experiment", "test", ] description_lower = project_description.lower() if any(keyword in description_lower for keyword in skip_keywords): return False return True
[docs] def enhance_project_with_documentation( tasks: List[Task], project_description: str, project_name: str = "Project" ) -> List[Task]: """ Add documentation task to project if appropriate. Parameters ---------- tasks : List[Task] Original project tasks project_description : str Project description project_name : str Name of the project Returns ------- List[Task] Task list with documentation task added if appropriate """ # Import here to avoid circular imports from src.integrations.adaptive_documentation import ( AdaptiveDocumentationGenerator, create_documentation_context, ) # Use adaptive generator for more intelligent documentation adaptive_generator = AdaptiveDocumentationGenerator() # Create context for documentation decisions context = create_documentation_context( tasks=tasks, project_name=project_name, source_type="nlp_project", # Default for now metadata={"description": project_description}, ) # Check if we should add documentation if not adaptive_generator.should_add_documentation(context): logger.info("Skipping documentation task for this project") return tasks # Generate appropriate documentation tasks doc_tasks = adaptive_generator.create_documentation_tasks(context) if doc_tasks: for task in doc_tasks: logger.info(f"Added documentation task: {task.name}") return tasks + doc_tasks # Fallback to legacy behavior if no tasks generated generator = DocumentationTaskGenerator() doc_task = generator.create_documentation_task(tasks, project_name) if doc_task: # Extract feature labels from tasks to add to doc task feature_labels = set() for task in tasks: if task.labels: for label in task.labels: if ( label.startswith("feature:") or label.startswith("component:") or label in ["backend", "frontend", "api", "database"] ): feature_labels.add(label) # Add feature labels to documentation task for phase enforcement if feature_labels: doc_task.labels = list(set(doc_task.labels) | feature_labels) logger.info(f"Added documentation task: {doc_task.name}") return tasks + [doc_task] return tasks
# Template for README.md README_TEMPLATE = """# README.md ## Project: {project_name} ### How It Works **Architecture Overview:** {architecture_description} **Key Components:** {component_list} **Data Flow:** {data_flow_description} **Key Technical Decisions:** {decisions_summary} ### How to Run It **Prerequisites:** {prerequisites} **Setup Steps:** ```bash {setup_commands} ``` **Configuration:** {config_instructions} **Starting the Application:** ```bash {run_commands} ``` **Verify It's Working:** {verification_steps} ### How to Test It **Running Tests:** ```bash {test_commands} ``` **Expected Output:** ``` {expected_test_output} ``` **Test Coverage:** {coverage_info} ### How to Deploy It **Build for Production:** ```bash {build_commands} ``` **Deployment Steps:** {deployment_steps} **Environment Variables:** {env_vars} **Health Check:** {health_check_info} ### Troubleshooting **Common Issues:** {common_issues} ### Implementation Notes **Logged Decisions:** {all_logged_decisions} --- *Documentation generated on {timestamp}* """