refactor: Standardize error handling patterns across codebase

Comprehensive error handling improvements addressing inconsistent patterns:

• Created markitect/exceptions.py with complete domain-specific exception hierarchy
  - MarkitectError base class with context and cause chaining support
  - Specific exceptions for Document, AST, Cache, Database, Schema operations
  - Built-in logging and context preservation

• Fixed overly broad exception handling in tddai modules:
  - issue_fetcher.py: Replace generic Exception with specific Gitea errors
  - project_manager.py: Proper error translation with context preservation
  - coverage_analyzer.py: Replace silent suppression with logging

• Enhanced cache_service.py error handling:
  - Specific OSError/PermissionError handling for file operations
  - Logging integration for unexpected errors
  - Preserved error collection and reporting

• Implemented proper exception chaining patterns:
  - All error translations use `raise ... from e` for debugging
  - Preserved original exception context and stack traces
  - Added docstring declarations of raised exceptions

• Benefits:
  - Eliminates silent error suppression and debugging black holes
  - Provides specific, actionable error messages
  - Preserves full error context for troubleshooting
  - Establishes consistent patterns for future development

Resolves issue #21: Error handling standardization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-26 16:35:13 +02:00
parent 7f5309c4b0
commit bbc6192fe1
7 changed files with 420 additions and 18 deletions

View File

@@ -166,6 +166,118 @@ class IssueView:
print(f" - Use 'make tdd-start NUM={result['number']}' to begin work")
print(f" - Use 'make show-issue NUM={result['number']}' to view details")
@staticmethod
def show_issue_details(issue_data: Dict[str, Any]) -> None:
"""Display comprehensive issue details."""
OutputFormatter.header(f"Issue #{issue_data['number']} Details", "=")
print(f"**Title:** {issue_data['title']}")
print(f"**Status:** {issue_data['state'].upper()}")
print(f"**Number:** #{issue_data['number']}")
print(f"**Created:** {issue_data['created_at'].strftime('%Y-%m-%d %H:%M')}")
print(f"**Updated:** {issue_data['updated_at'].strftime('%Y-%m-%d %H:%M')}")
print(f"**URL:** {issue_data['html_url']}")
if issue_data['assignee']:
print(f"**Assignee:** {issue_data['assignee']}")
OutputFormatter.empty_line()
print("**Project Management:**")
# Milestone information
if issue_data['milestone']:
milestone = issue_data['milestone']
print(f" 📋 Milestone: #{milestone['id']} - {milestone['title']} ({milestone['state']})")
else:
print(" 📋 Milestone: None")
# Project/Board information
print(" 🎯 Project: Getting Started (assumed - requires board API)")
# Labels and state information
print(f" 📊 State: {issue_data['state_label']}")
print(f" 🚨 Priority: {issue_data['priority_label']}")
if issue_data['type_labels']:
type_display = ', '.join(issue_data['type_labels'])
print(f" 🏷️ Type: {type_display}")
if issue_data['other_labels']:
print(f" 🏷️ Other Labels: {', '.join(issue_data['other_labels'])}")
print(f" 📝 Kanban Column: {issue_data['kanban_column']}")
OutputFormatter.empty_line()
print("**Description:**")
print(issue_data['body'])
OutputFormatter.empty_line()
print("💡 Tip: Use 'make list-issues' to see all issues")
@staticmethod
def show_coverage_analysis(coverage_data: Dict[str, Any]) -> None:
"""Display test coverage analysis results."""
OutputFormatter.header(f"Test Coverage Analysis for Issue #{coverage_data['issue_number']}", "=")
print(f"📋 Issue: #{coverage_data['issue_number']} - {coverage_data['issue_title']}")
print(f"📊 Coverage: {coverage_data['coverage_percentage']:.1f}%")
OutputFormatter.empty_line()
# Show requirements analysis
print("🎯 Identified Requirements:")
if coverage_data['requirements']:
for req in coverage_data['requirements']:
priority_icon = {"critical": "🚨", "important": "⚠️", "nice-to-have": "💡"}
icon = priority_icon.get(req.priority, "📝")
print(f" {icon} [{req.priority.upper()}] {req.category}: {req.description}")
else:
print(" No specific requirements detected")
OutputFormatter.empty_line()
# Show existing tests
print("🧪 Existing Test Coverage:")
issue_number = coverage_data['issue_number']
issue_related_tests = [t for t in coverage_data['existing_tests'] if t.related_issue == issue_number]
if issue_related_tests:
for test in issue_related_tests:
test_count = len(test.test_methods)
print(f"{test.file_path.name} ({test_count} test methods)")
if test.test_methods:
for method in test.test_methods[:3]: # Show first 3
print(f" - {method}")
if len(test.test_methods) > 3:
print(f" - ... and {len(test.test_methods) - 3} more")
else:
print(" 📝 No tests specifically for this issue found")
# Show general tests that might be relevant
relevant_tests = [t for t in coverage_data['existing_tests']
if any(keyword in ' '.join(t.coverage_keywords)
for req in coverage_data['requirements']
for keyword in req.keywords)]
if relevant_tests:
print(" 📋 Potentially relevant tests:")
for test in relevant_tests[:3]:
print(f" 📄 {test.file_path.name}")
OutputFormatter.empty_line()
# Show coverage gaps
if coverage_data['coverage_gaps']:
print("❌ Coverage Gaps Found:")
for gap in coverage_data['coverage_gaps']:
priority_icon = {"critical": "🚨", "important": "⚠️", "nice-to-have": "💡"}
icon = priority_icon.get(gap.requirement.priority, "📝")
print(f" {icon} Missing: {gap.requirement.description}")
print(f" 💡 Suggested test: {gap.suggested_test_name}")
print(f" 📄 Suggested file: {gap.suggested_test_file}")
OutputFormatter.empty_line()
else:
print("✅ No significant coverage gaps detected!")
OutputFormatter.empty_line()
# Show recommendations
print("📝 Recommendations:")
for recommendation in coverage_data['recommendations']:
print(f" {recommendation}")
class ProjectView:
"""View for project management information display."""