roadmap-cli 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roadmap/__init__.py +27 -0
- roadmap/adapters/__init__.py +1 -0
- roadmap/adapters/base_paginated_adapter.py +117 -0
- roadmap/adapters/cli/__init__.py +249 -0
- roadmap/adapters/cli/analysis/__init__.py +5 -0
- roadmap/adapters/cli/analysis/commands.py +370 -0
- roadmap/adapters/cli/analysis/presenter.py +125 -0
- roadmap/adapters/cli/archive_operations.py +86 -0
- roadmap/adapters/cli/cli_command_helpers.py +109 -0
- roadmap/adapters/cli/cli_confirmations.py +74 -0
- roadmap/adapters/cli/cli_error_handlers.py +146 -0
- roadmap/adapters/cli/cli_validators.py +39 -0
- roadmap/adapters/cli/commands/sync_status.py +308 -0
- roadmap/adapters/cli/comment/__init__.py +5 -0
- roadmap/adapters/cli/comment/commands.py +106 -0
- roadmap/adapters/cli/config/__init__.py +5 -0
- roadmap/adapters/cli/config/commands.py +198 -0
- roadmap/adapters/cli/console_exports.py +10 -0
- roadmap/adapters/cli/core.py +26 -0
- roadmap/adapters/cli/crud/__init__.py +47 -0
- roadmap/adapters/cli/crud/base_archive.py +208 -0
- roadmap/adapters/cli/crud/base_create.py +154 -0
- roadmap/adapters/cli/crud/base_delete.py +155 -0
- roadmap/adapters/cli/crud/base_restore.py +283 -0
- roadmap/adapters/cli/crud/base_update.py +193 -0
- roadmap/adapters/cli/crud/crud_helpers.py +206 -0
- roadmap/adapters/cli/crud/crud_utils.py +152 -0
- roadmap/adapters/cli/crud/entity_builders.py +374 -0
- roadmap/adapters/cli/data/__init__.py +8 -0
- roadmap/adapters/cli/data/commands.py +149 -0
- roadmap/adapters/cli/decorators.py +208 -0
- roadmap/adapters/cli/dtos/__init__.py +155 -0
- roadmap/adapters/cli/exception_handler.py +105 -0
- roadmap/adapters/cli/git/__init__.py +8 -0
- roadmap/adapters/cli/git/commands.py +475 -0
- roadmap/adapters/cli/git/handlers/__init__.py +1 -0
- roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
- roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
- roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
- roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
- roadmap/adapters/cli/git/hooks_config.py +327 -0
- roadmap/adapters/cli/git/status_display.py +158 -0
- roadmap/adapters/cli/health/__init__.py +8 -0
- roadmap/adapters/cli/health/db_integrity.py +204 -0
- roadmap/adapters/cli/health/enhancer.py +227 -0
- roadmap/adapters/cli/health/fixer.py +295 -0
- roadmap/adapters/cli/health/fixers/__init__.py +10 -0
- roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
- roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
- roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
- roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
- roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
- roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
- roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
- roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
- roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
- roadmap/adapters/cli/health/formatter.py +250 -0
- roadmap/adapters/cli/health/formatters.py +427 -0
- roadmap/adapters/cli/health/scan.py +293 -0
- roadmap/adapters/cli/init/__init__.py +5 -0
- roadmap/adapters/cli/init/commands.py +587 -0
- roadmap/adapters/cli/init.py +9 -0
- roadmap/adapters/cli/issues/__init__.py +70 -0
- roadmap/adapters/cli/issues/archive.py +137 -0
- roadmap/adapters/cli/issues/archive_class.py +253 -0
- roadmap/adapters/cli/issues/block.py +39 -0
- roadmap/adapters/cli/issues/close.py +216 -0
- roadmap/adapters/cli/issues/comment.py +178 -0
- roadmap/adapters/cli/issues/create.py +162 -0
- roadmap/adapters/cli/issues/delete.py +31 -0
- roadmap/adapters/cli/issues/deps.py +161 -0
- roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
- roadmap/adapters/cli/issues/link.py +308 -0
- roadmap/adapters/cli/issues/list.py +402 -0
- roadmap/adapters/cli/issues/lookup.py +96 -0
- roadmap/adapters/cli/issues/progress.py +102 -0
- roadmap/adapters/cli/issues/restore.py +96 -0
- roadmap/adapters/cli/issues/restore_class.py +57 -0
- roadmap/adapters/cli/issues/start.py +165 -0
- roadmap/adapters/cli/issues/sync_status.py +374 -0
- roadmap/adapters/cli/issues/unblock.py +43 -0
- roadmap/adapters/cli/issues/unlink.py +104 -0
- roadmap/adapters/cli/issues/update.py +120 -0
- roadmap/adapters/cli/issues/view.py +36 -0
- roadmap/adapters/cli/layout.py +220 -0
- roadmap/adapters/cli/mappers.py +200 -0
- roadmap/adapters/cli/milestones/__init__.py +52 -0
- roadmap/adapters/cli/milestones/archive.py +125 -0
- roadmap/adapters/cli/milestones/archive_class.py +236 -0
- roadmap/adapters/cli/milestones/assign.py +62 -0
- roadmap/adapters/cli/milestones/close.py +177 -0
- roadmap/adapters/cli/milestones/create.py +131 -0
- roadmap/adapters/cli/milestones/delete.py +27 -0
- roadmap/adapters/cli/milestones/kanban.py +123 -0
- roadmap/adapters/cli/milestones/list.py +87 -0
- roadmap/adapters/cli/milestones/recalculate.py +79 -0
- roadmap/adapters/cli/milestones/restore.py +87 -0
- roadmap/adapters/cli/milestones/restore_class.py +99 -0
- roadmap/adapters/cli/milestones/update.py +61 -0
- roadmap/adapters/cli/milestones/view.py +142 -0
- roadmap/adapters/cli/output_manager.py +319 -0
- roadmap/adapters/cli/presentation/base_presenter.py +121 -0
- roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
- roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
- roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
- roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
- roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
- roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
- roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
- roadmap/adapters/cli/presentation/project_presenter.py +233 -0
- roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
- roadmap/adapters/cli/presentation/table_builders.py +95 -0
- roadmap/adapters/cli/projects/__init__.py +42 -0
- roadmap/adapters/cli/projects/archive.py +127 -0
- roadmap/adapters/cli/projects/archive_class.py +96 -0
- roadmap/adapters/cli/projects/close.py +94 -0
- roadmap/adapters/cli/projects/create.py +50 -0
- roadmap/adapters/cli/projects/delete.py +27 -0
- roadmap/adapters/cli/projects/list.py +227 -0
- roadmap/adapters/cli/projects/restore.py +87 -0
- roadmap/adapters/cli/projects/restore_class.py +56 -0
- roadmap/adapters/cli/projects/update.py +57 -0
- roadmap/adapters/cli/projects/view.py +157 -0
- roadmap/adapters/cli/services/__init__.py +1 -0
- roadmap/adapters/cli/services/daily_summary_service.py +313 -0
- roadmap/adapters/cli/services/export_manager.py +268 -0
- roadmap/adapters/cli/services/milestone_list_service.py +266 -0
- roadmap/adapters/cli/services/project_initialization_service.py +29 -0
- roadmap/adapters/cli/services/project_status_service.py +462 -0
- roadmap/adapters/cli/services/sync_service.py +62 -0
- roadmap/adapters/cli/status.py +408 -0
- roadmap/adapters/cli/styling.py +21 -0
- roadmap/adapters/cli/sync.py +1117 -0
- roadmap/adapters/cli/sync_context.py +432 -0
- roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
- roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
- roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
- roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
- roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
- roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
- roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
- roadmap/adapters/cli/sync_metrics_command.py +190 -0
- roadmap/adapters/cli/sync_presenter.py +58 -0
- roadmap/adapters/cli/sync_validation.py +295 -0
- roadmap/adapters/cli/today.py +52 -0
- roadmap/adapters/cli/utils/click_options.py +38 -0
- roadmap/adapters/git/__init__.py +1 -0
- roadmap/adapters/git/git.py +208 -0
- roadmap/adapters/git/git_branch_manager.py +400 -0
- roadmap/adapters/git/git_command_executor.py +56 -0
- roadmap/adapters/git/git_commit_analyzer.py +334 -0
- roadmap/adapters/git/git_hooks.py +22 -0
- roadmap/adapters/git/git_hooks_manager.py +455 -0
- roadmap/adapters/git/git_repository_info.py +65 -0
- roadmap/adapters/git/hook_installer.py +106 -0
- roadmap/adapters/git/hook_registry.py +97 -0
- roadmap/adapters/git/hook_script_generator.py +58 -0
- roadmap/adapters/git/sync_monitor.py +409 -0
- roadmap/adapters/git/workflow_automation.py +286 -0
- roadmap/adapters/github/__init__.py +1 -0
- roadmap/adapters/github/github.py +360 -0
- roadmap/adapters/github/handlers/__init__.py +17 -0
- roadmap/adapters/github/handlers/base.py +337 -0
- roadmap/adapters/github/handlers/collaborators.py +165 -0
- roadmap/adapters/github/handlers/comments.py +118 -0
- roadmap/adapters/github/handlers/issues.py +150 -0
- roadmap/adapters/github/handlers/labels.py +164 -0
- roadmap/adapters/github/handlers/milestones.py +104 -0
- roadmap/adapters/persistence/__init__.py +35 -0
- roadmap/adapters/persistence/conflict_resolver.py +190 -0
- roadmap/adapters/persistence/database_manager.py +492 -0
- roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
- roadmap/adapters/persistence/file_locking.py +348 -0
- roadmap/adapters/persistence/file_parser.py +92 -0
- roadmap/adapters/persistence/file_synchronizer.py +107 -0
- roadmap/adapters/persistence/focused_managers.py +140 -0
- roadmap/adapters/persistence/git_history.py +330 -0
- roadmap/adapters/persistence/parser/__init__.py +20 -0
- roadmap/adapters/persistence/parser/frontmatter.py +152 -0
- roadmap/adapters/persistence/parser/issue.py +245 -0
- roadmap/adapters/persistence/parser/milestone.py +146 -0
- roadmap/adapters/persistence/parser/project.py +106 -0
- roadmap/adapters/persistence/persistence.py +288 -0
- roadmap/adapters/persistence/repositories/__init__.py +15 -0
- roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
- roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
- roadmap/adapters/persistence/repositories/project_repository.py +168 -0
- roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
- roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
- roadmap/adapters/persistence/storage/__init__.py +36 -0
- roadmap/adapters/persistence/storage/conflicts.py +101 -0
- roadmap/adapters/persistence/storage/connection_manager.py +79 -0
- roadmap/adapters/persistence/storage/issue_storage.py +89 -0
- roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
- roadmap/adapters/persistence/storage/project_storage.py +102 -0
- roadmap/adapters/persistence/storage/queries.py +227 -0
- roadmap/adapters/persistence/storage/state_manager.py +652 -0
- roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
- roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
- roadmap/adapters/persistence/sync_orchestrator.py +293 -0
- roadmap/adapters/persistence/sync_state_tracker.py +86 -0
- roadmap/adapters/persistence/yaml_repositories.py +711 -0
- roadmap/adapters/sync/__init__.py +21 -0
- roadmap/adapters/sync/backend_factory.py +108 -0
- roadmap/adapters/sync/backends/__init__.py +15 -0
- roadmap/adapters/sync/backends/converters.py +209 -0
- roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
- roadmap/adapters/sync/backends/github_client.py +99 -0
- roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
- roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
- roadmap/adapters/sync/backends/services/__init__.py +1 -0
- roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
- roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
- roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
- roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
- roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
- roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
- roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
- roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
- roadmap/adapters/sync/services/__init__.py +11 -0
- roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
- roadmap/adapters/sync/services/conflict_converter.py +85 -0
- roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
- roadmap/adapters/sync/services/issue_state_service.py +243 -0
- roadmap/adapters/sync/services/local_change_filter.py +129 -0
- roadmap/adapters/sync/services/pull_result_processor.py +71 -0
- roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
- roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
- roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
- roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
- roadmap/adapters/sync/services/sync_linking_service.py +329 -0
- roadmap/adapters/sync/services/sync_report_service.py +62 -0
- roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
- roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
- roadmap/adapters/sync/sync_merge_engine.py +416 -0
- roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
- roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
- roadmap/application/services/__init__.py +1 -0
- roadmap/application/services/deduplicate_service.py +517 -0
- roadmap/application/use_cases/__init__.py +1 -0
- roadmap/common/__init__.py +308 -0
- roadmap/common/cache.py +209 -0
- roadmap/common/cli_errors.py +147 -0
- roadmap/common/configuration/__init__.py +33 -0
- roadmap/common/configuration/config_loader.py +264 -0
- roadmap/common/configuration/config_manager.py +164 -0
- roadmap/common/configuration/config_schema.py +145 -0
- roadmap/common/configuration/github/__init__.py +9 -0
- roadmap/common/configuration/github/config_manager.py +154 -0
- roadmap/common/configuration/github/token_resolver.py +84 -0
- roadmap/common/console.py +184 -0
- roadmap/common/constants.py +134 -0
- roadmap/common/datetime_parser.py +319 -0
- roadmap/common/error_formatter.py +84 -0
- roadmap/common/errors/__init__.py +143 -0
- roadmap/common/errors/error_base.py +73 -0
- roadmap/common/errors/error_file.py +82 -0
- roadmap/common/errors/error_git.py +67 -0
- roadmap/common/errors/error_handler.py +166 -0
- roadmap/common/errors/error_network.py +90 -0
- roadmap/common/errors/error_security.py +78 -0
- roadmap/common/errors/error_standards.py +561 -0
- roadmap/common/errors/error_validation.py +111 -0
- roadmap/common/errors/exceptions.py +432 -0
- roadmap/common/formatters/__init__.py +29 -0
- roadmap/common/formatters/base_table_formatter.py +96 -0
- roadmap/common/formatters/export/__init__.py +7 -0
- roadmap/common/formatters/export/issue_exporter.py +97 -0
- roadmap/common/formatters/helpers.py +28 -0
- roadmap/common/formatters/kanban/__init__.py +9 -0
- roadmap/common/formatters/kanban/layout.py +48 -0
- roadmap/common/formatters/kanban/organizer.py +81 -0
- roadmap/common/formatters/output/__init__.py +15 -0
- roadmap/common/formatters/tables/__init__.py +11 -0
- roadmap/common/formatters/tables/column_factory.py +312 -0
- roadmap/common/formatters/tables/issue_table.py +276 -0
- roadmap/common/formatters/tables/milestone_table.py +247 -0
- roadmap/common/formatters/tables/project_table.py +195 -0
- roadmap/common/formatters/text/__init__.py +57 -0
- roadmap/common/formatters/text/basic.py +217 -0
- roadmap/common/formatters/text/display.py +42 -0
- roadmap/common/formatters/text/duration.py +36 -0
- roadmap/common/formatters/text/operations.py +325 -0
- roadmap/common/formatters/text/status_badges.py +57 -0
- roadmap/common/initialization/github/__init__.py +11 -0
- roadmap/common/initialization/github/setup_service.py +238 -0
- roadmap/common/initialization/github/setup_validator.py +78 -0
- roadmap/common/logging/__init__.py +113 -0
- roadmap/common/logging/audit_logging.py +5 -0
- roadmap/common/logging/correlation.py +45 -0
- roadmap/common/logging/decorators.py +267 -0
- roadmap/common/logging/error_logging.py +217 -0
- roadmap/common/logging/formatters.py +124 -0
- roadmap/common/logging/loggers.py +68 -0
- roadmap/common/logging/performance_tracking.py +267 -0
- roadmap/common/logging/utils.py +335 -0
- roadmap/common/models/__init__.py +44 -0
- roadmap/common/models/cli_models.py +118 -0
- roadmap/common/models/config_models.py +199 -0
- roadmap/common/models/output_models.py +381 -0
- roadmap/common/observability/__init__.py +28 -0
- roadmap/common/observability/instrumentation.py +103 -0
- roadmap/common/observability/observability.py +76 -0
- roadmap/common/observability/otel_init.py +109 -0
- roadmap/common/output_formatter.py +393 -0
- roadmap/common/progress.py +379 -0
- roadmap/common/result.py +396 -0
- roadmap/common/security/__init__.py +48 -0
- roadmap/common/security/exceptions.py +13 -0
- roadmap/common/security/export_cleanup.py +79 -0
- roadmap/common/security/file_operations.py +104 -0
- roadmap/common/security/filename_sanitization.py +54 -0
- roadmap/common/security/logging.py +27 -0
- roadmap/common/security/path_validation.py +137 -0
- roadmap/common/security/temp_files.py +41 -0
- roadmap/common/services/__init__.py +65 -0
- roadmap/common/services/decorators.py +183 -0
- roadmap/common/services/logging_utils.py +208 -0
- roadmap/common/services/metrics.py +161 -0
- roadmap/common/services/performance.py +219 -0
- roadmap/common/services/profiling.py +288 -0
- roadmap/common/services/retry.py +288 -0
- roadmap/common/status_style_manager.py +114 -0
- roadmap/common/union_find.py +88 -0
- roadmap/common/update_constants.py +13 -0
- roadmap/common/utils/__init__.py +65 -0
- roadmap/common/utils/cli_helpers.py +381 -0
- roadmap/common/utils/file_utils.py +440 -0
- roadmap/common/utils/path_utils.py +38 -0
- roadmap/common/utils/status_utils.py +130 -0
- roadmap/common/utils/timezone_utils.py +480 -0
- roadmap/common/validation/__init__.py +37 -0
- roadmap/common/validation/field_validator.py +104 -0
- roadmap/common/validation/result.py +37 -0
- roadmap/common/validation/roadmap_validator.py +310 -0
- roadmap/common/validation/schema_validator.py +36 -0
- roadmap/common/validation/validators.py +50 -0
- roadmap/core/__init__.py +1 -0
- roadmap/core/domain/__init__.py +30 -0
- roadmap/core/domain/comment.py +24 -0
- roadmap/core/domain/health.py +15 -0
- roadmap/core/domain/issue.py +274 -0
- roadmap/core/domain/milestone.py +173 -0
- roadmap/core/domain/ports/__init__.py +1 -0
- roadmap/core/domain/ports/baseline_repository.py +24 -0
- roadmap/core/domain/ports/issue_repository.py +34 -0
- roadmap/core/domain/ports/remote_backend_port.py +31 -0
- roadmap/core/domain/project.py +130 -0
- roadmap/core/interfaces/__init__.py +109 -0
- roadmap/core/interfaces/assignee_validator.py +40 -0
- roadmap/core/interfaces/backend_factory.py +175 -0
- roadmap/core/interfaces/github.py +102 -0
- roadmap/core/interfaces/parsers.py +140 -0
- roadmap/core/interfaces/persistence.py +45 -0
- roadmap/core/interfaces/repositories.py +174 -0
- roadmap/core/interfaces/state_managers.py +111 -0
- roadmap/core/interfaces/state_storage.py +64 -0
- roadmap/core/interfaces/sync_backend.py +275 -0
- roadmap/core/interfaces/sync_services.py +119 -0
- roadmap/core/interfaces/sync_validators.py +180 -0
- roadmap/core/models/__init__.py +31 -0
- roadmap/core/models/sync_models.py +162 -0
- roadmap/core/models.py +115 -0
- roadmap/core/observability/__init__.py +17 -0
- roadmap/core/observability/sync_metrics.py +699 -0
- roadmap/core/repositories.py +200 -0
- roadmap/core/services/__init__.py +313 -0
- roadmap/core/services/baseline/__init__.py +1 -0
- roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
- roadmap/core/services/baseline/baseline_retriever.py +63 -0
- roadmap/core/services/baseline/baseline_selector.py +248 -0
- roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
- roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
- roadmap/core/services/comment/__init__.py +1 -0
- roadmap/core/services/comment/comment_service.py +277 -0
- roadmap/core/services/git/__init__.py +1 -0
- roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
- roadmap/core/services/github/__init__.py +1 -0
- roadmap/core/services/github/github_change_detector.py +162 -0
- roadmap/core/services/github/github_config_validator.py +148 -0
- roadmap/core/services/github/github_conflict_detector.py +179 -0
- roadmap/core/services/github/github_entity_classifier.py +51 -0
- roadmap/core/services/github/github_integration_service.py +439 -0
- roadmap/core/services/github/github_issue_client.py +346 -0
- roadmap/core/services/health/__init__.py +1 -0
- roadmap/core/services/health/backup_cleanup_service.py +202 -0
- roadmap/core/services/health/data_integrity_validator_service.py +90 -0
- roadmap/core/services/health/entity_health_scanner.py +460 -0
- roadmap/core/services/health/file_repair_service.py +193 -0
- roadmap/core/services/health/health_check_service.py +166 -0
- roadmap/core/services/health/health_models.py +80 -0
- roadmap/core/services/health/infrastructure_validator_service.py +328 -0
- roadmap/core/services/health/issue_health_scanner.py +263 -0
- roadmap/core/services/helpers/__init__.py +18 -0
- roadmap/core/services/helpers/status_change_helpers.py +88 -0
- roadmap/core/services/initialization/__init__.py +19 -0
- roadmap/core/services/initialization/utils.py +104 -0
- roadmap/core/services/initialization/validator.py +78 -0
- roadmap/core/services/initialization/workflow.py +182 -0
- roadmap/core/services/initialization_service.py +111 -0
- roadmap/core/services/issue/__init__.py +1 -0
- roadmap/core/services/issue/assignee_validation_service.py +312 -0
- roadmap/core/services/issue/issue_creation_service.py +309 -0
- roadmap/core/services/issue/issue_filter_service.py +302 -0
- roadmap/core/services/issue/issue_matching_service.py +127 -0
- roadmap/core/services/issue/issue_service.py +765 -0
- roadmap/core/services/issue/issue_update_service.py +144 -0
- roadmap/core/services/issue/start_issue_service.py +174 -0
- roadmap/core/services/issue_helpers/__init__.py +18 -0
- roadmap/core/services/issue_helpers/issue_filters.py +296 -0
- roadmap/core/services/milestone_service.py +438 -0
- roadmap/core/services/project/__init__.py +1 -0
- roadmap/core/services/project/project_service.py +434 -0
- roadmap/core/services/project/project_status_service.py +125 -0
- roadmap/core/services/project_init/__init__.py +22 -0
- roadmap/core/services/project_init/context_detection.py +146 -0
- roadmap/core/services/project_init/creation.py +76 -0
- roadmap/core/services/project_init/detection.py +51 -0
- roadmap/core/services/project_init/template.py +228 -0
- roadmap/core/services/status_change_service.py +88 -0
- roadmap/core/services/sync/__init__.py +1 -0
- roadmap/core/services/sync/batch_processor.py +290 -0
- roadmap/core/services/sync/conflict_resolver.py +154 -0
- roadmap/core/services/sync/dependency_resolver.py +367 -0
- roadmap/core/services/sync/duplicate_detector.py +771 -0
- roadmap/core/services/sync/duplicate_resolver.py +315 -0
- roadmap/core/services/sync/error_classification.py +470 -0
- roadmap/core/services/sync/performance.py +263 -0
- roadmap/core/services/sync/sync_change_computer.py +211 -0
- roadmap/core/services/sync/sync_checkpoint.py +348 -0
- roadmap/core/services/sync/sync_conflict_detector.py +147 -0
- roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
- roadmap/core/services/sync/sync_errors.py +383 -0
- roadmap/core/services/sync/sync_key_normalizer.py +222 -0
- roadmap/core/services/sync/sync_metadata_service.py +361 -0
- roadmap/core/services/sync/sync_plan.py +171 -0
- roadmap/core/services/sync/sync_plan_executor.py +447 -0
- roadmap/core/services/sync/sync_report.py +296 -0
- roadmap/core/services/sync/sync_state.py +173 -0
- roadmap/core/services/sync/sync_state_comparator.py +690 -0
- roadmap/core/services/sync/sync_state_manager.py +431 -0
- roadmap/core/services/sync/sync_state_normalizer.py +98 -0
- roadmap/core/services/sync/sync_three_way.py +78 -0
- roadmap/core/services/sync/three_way_merger.py +178 -0
- roadmap/core/services/utils/__init__.py +1 -0
- roadmap/core/services/utils/configuration_service.py +113 -0
- roadmap/core/services/utils/critical_path_calculator.py +418 -0
- roadmap/core/services/utils/dependency_analyzer.py +360 -0
- roadmap/core/services/utils/field_conflict_detector.py +126 -0
- roadmap/core/services/utils/remote_fetcher.py +202 -0
- roadmap/core/services/utils/remote_state_normalizer.py +110 -0
- roadmap/core/services/utils/retry_policy.py +51 -0
- roadmap/core/services/validator_base.py +130 -0
- roadmap/core/services/validators/__init__.py +28 -0
- roadmap/core/services/validators/_utils.py +20 -0
- roadmap/core/services/validators/archivable_issues_validator.py +71 -0
- roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
- roadmap/core/services/validators/backup_validator.py +89 -0
- roadmap/core/services/validators/data_integrity_validator.py +84 -0
- roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
- roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
- roadmap/core/services/validators/folder_structure_validator.py +165 -0
- roadmap/core/services/validators/health_status_utils.py +28 -0
- roadmap/core/services/validators/milestone_naming_validator.py +146 -0
- roadmap/core/services/validators/missing_headlines_validator.py +97 -0
- roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
- roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
- roadmap/core/utils/git_remote_parser.py +83 -0
- roadmap/core/utils/github_urls.py +66 -0
- roadmap/domain/validation.py +30 -0
- roadmap/infrastructure/__init__.py +55 -0
- roadmap/infrastructure/coordination/__init__.py +8 -0
- roadmap/infrastructure/coordination/coordinator_params.py +82 -0
- roadmap/infrastructure/coordination/core.py +446 -0
- roadmap/infrastructure/coordination/git_coordinator.py +160 -0
- roadmap/infrastructure/coordination/initialization.py +348 -0
- roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
- roadmap/infrastructure/coordination/issue_operations.py +456 -0
- roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
- roadmap/infrastructure/coordination/milestone_operations.py +201 -0
- roadmap/infrastructure/coordination/project_coordinator.py +113 -0
- roadmap/infrastructure/coordination/project_operations.py +144 -0
- roadmap/infrastructure/coordination/team_coordinator.py +107 -0
- roadmap/infrastructure/coordination/user_operations.py +150 -0
- roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
- roadmap/infrastructure/coordination_gateway.py +152 -0
- roadmap/infrastructure/git/__init__.py +6 -0
- roadmap/infrastructure/git/git_integration_ops.py +234 -0
- roadmap/infrastructure/git_gateway.py +35 -0
- roadmap/infrastructure/github_gateway.py +52 -0
- roadmap/infrastructure/maintenance/__init__.py +5 -0
- roadmap/infrastructure/maintenance/cleanup.py +483 -0
- roadmap/infrastructure/observability/__init__.py +7 -0
- roadmap/infrastructure/observability/health.py +196 -0
- roadmap/infrastructure/observability/health_formatter.py +73 -0
- roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
- roadmap/infrastructure/persistence_gateway.py +164 -0
- roadmap/infrastructure/security/__init__.py +1 -0
- roadmap/infrastructure/security/credentials.py +489 -0
- roadmap/infrastructure/sync_gateway.py +94 -0
- roadmap/infrastructure/validation/__init__.py +7 -0
- roadmap/infrastructure/validation/file_enumeration.py +219 -0
- roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
- roadmap/infrastructure/validation/github_validator.py +101 -0
- roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
- roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
- roadmap/infrastructure/validation_gateway.py +96 -0
- roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
- roadmap/settings.py +345 -0
- roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
- roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
- roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
- roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
- roadmap/version.py +381 -0
- roadmap_cli-0.1.1.dist-info/METADATA +448 -0
- roadmap_cli-0.1.1.dist-info/RECORD +521 -0
- roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
- roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
- roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
"""Error classification system for sync operations.
|
|
2
|
+
|
|
3
|
+
This module provides categorization and aggregation of sync errors
|
|
4
|
+
to present clear, actionable error summaries to users.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from roadmap.common.logging import get_logger
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ErrorCategory(StrEnum):
|
|
18
|
+
"""Categories of sync errors for classification and reporting."""
|
|
19
|
+
|
|
20
|
+
# Dependency/relationship errors
|
|
21
|
+
MILESTONE_NOT_FOUND = "milestone_not_found"
|
|
22
|
+
PROJECT_NOT_FOUND = "project_not_found"
|
|
23
|
+
DEPENDENCY_MISSING = "dependency_missing"
|
|
24
|
+
FOREIGN_KEY_CONSTRAINT = "foreign_key_constraint"
|
|
25
|
+
|
|
26
|
+
# API/network errors
|
|
27
|
+
API_RATE_LIMIT = "api_rate_limit"
|
|
28
|
+
NETWORK_ERROR = "network_error"
|
|
29
|
+
TIMEOUT = "timeout"
|
|
30
|
+
SERVICE_UNAVAILABLE = "service_unavailable"
|
|
31
|
+
|
|
32
|
+
# Authentication/authorization errors
|
|
33
|
+
AUTHENTICATION_FAILED = "authentication_failed"
|
|
34
|
+
PERMISSION_DENIED = "permission_denied"
|
|
35
|
+
TOKEN_EXPIRED = "token_expired"
|
|
36
|
+
|
|
37
|
+
# Data/validation errors
|
|
38
|
+
INVALID_DATA = "invalid_data"
|
|
39
|
+
SCHEMA_MISMATCH = "schema_mismatch"
|
|
40
|
+
DUPLICATE_ENTITY = "duplicate_entity"
|
|
41
|
+
|
|
42
|
+
# Resource errors
|
|
43
|
+
RESOURCE_DELETED = "resource_deleted"
|
|
44
|
+
RESOURCE_NOT_FOUND = "resource_not_found"
|
|
45
|
+
|
|
46
|
+
# System errors
|
|
47
|
+
DATABASE_ERROR = "database_error"
|
|
48
|
+
FILE_SYSTEM_ERROR = "file_system_error"
|
|
49
|
+
|
|
50
|
+
# Unknown/other
|
|
51
|
+
UNKNOWN_ERROR = "unknown_error"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class ErrorDetails:
|
|
56
|
+
"""Detailed information about a specific error occurrence."""
|
|
57
|
+
|
|
58
|
+
category: ErrorCategory
|
|
59
|
+
error_message: str
|
|
60
|
+
error_type: str
|
|
61
|
+
entity_type: str # "Issue", "Milestone", "Project"
|
|
62
|
+
entity_id: str | None = None
|
|
63
|
+
suggested_fix: str | None = None
|
|
64
|
+
is_recoverable: bool = True
|
|
65
|
+
metadata: dict[str, Any] | None = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class ErrorSummary:
|
|
70
|
+
"""Aggregated summary of errors by category."""
|
|
71
|
+
|
|
72
|
+
category: ErrorCategory
|
|
73
|
+
count: int
|
|
74
|
+
sample_messages: list[str] # Top 3-5 unique error messages
|
|
75
|
+
affected_entities: list[str] # Sample of affected entity IDs
|
|
76
|
+
suggested_fix: str
|
|
77
|
+
is_recoverable: bool
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ErrorClassifier:
|
|
81
|
+
"""Classifies and aggregates sync errors for user presentation."""
|
|
82
|
+
|
|
83
|
+
def __init__(self):
|
|
84
|
+
"""Initialize error classifier with empty state."""
|
|
85
|
+
self._errors: list[ErrorDetails] = []
|
|
86
|
+
self._category_counts: dict[ErrorCategory, int] = defaultdict(int)
|
|
87
|
+
|
|
88
|
+
def classify_error(
|
|
89
|
+
self,
|
|
90
|
+
error_message: str,
|
|
91
|
+
error_type: str,
|
|
92
|
+
entity_type: str,
|
|
93
|
+
entity_id: str | None = None,
|
|
94
|
+
metadata: dict[str, Any] | None = None,
|
|
95
|
+
) -> ErrorDetails:
|
|
96
|
+
"""Classify an error and determine its category.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
error_message: The error message text
|
|
100
|
+
error_type: The exception type (e.g., "IntegrityError", "ValueError")
|
|
101
|
+
entity_type: Type of entity affected (Issue, Milestone, Project)
|
|
102
|
+
entity_id: Identifier of affected entity
|
|
103
|
+
metadata: Additional context about the error
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
ErrorDetails with classified category and suggested fix
|
|
107
|
+
"""
|
|
108
|
+
category = self._determine_category(error_message, error_type, metadata)
|
|
109
|
+
suggested_fix = self._get_suggested_fix(category, error_message)
|
|
110
|
+
is_recoverable = self._is_recoverable(category)
|
|
111
|
+
|
|
112
|
+
error_details = ErrorDetails(
|
|
113
|
+
category=category,
|
|
114
|
+
error_message=error_message,
|
|
115
|
+
error_type=error_type,
|
|
116
|
+
entity_type=entity_type,
|
|
117
|
+
entity_id=entity_id,
|
|
118
|
+
suggested_fix=suggested_fix,
|
|
119
|
+
is_recoverable=is_recoverable,
|
|
120
|
+
metadata=metadata,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
self._errors.append(error_details)
|
|
124
|
+
self._category_counts[category] += 1
|
|
125
|
+
|
|
126
|
+
return error_details
|
|
127
|
+
|
|
128
|
+
def _determine_category(
|
|
129
|
+
self, error_message: str, error_type: str, metadata: dict[str, Any] | None
|
|
130
|
+
) -> ErrorCategory:
|
|
131
|
+
"""Determine error category from message and type.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
error_message: Error message text
|
|
135
|
+
error_type: Exception type name
|
|
136
|
+
metadata: Additional context
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Appropriate ErrorCategory
|
|
140
|
+
"""
|
|
141
|
+
msg_lower = error_message.lower()
|
|
142
|
+
type_lower = error_type.lower()
|
|
143
|
+
|
|
144
|
+
# Check categories in order of specificity
|
|
145
|
+
category = (
|
|
146
|
+
self._check_dependency_errors(msg_lower)
|
|
147
|
+
or self._check_api_errors(msg_lower)
|
|
148
|
+
or self._check_auth_errors(msg_lower)
|
|
149
|
+
or self._check_data_errors(msg_lower, type_lower)
|
|
150
|
+
or self._check_resource_errors(msg_lower)
|
|
151
|
+
or self._check_file_system_errors(type_lower)
|
|
152
|
+
)
|
|
153
|
+
return category or ErrorCategory.UNKNOWN_ERROR
|
|
154
|
+
|
|
155
|
+
def _check_dependency_errors(self, msg_lower: str) -> ErrorCategory | None:
|
|
156
|
+
"""Check for dependency-related errors."""
|
|
157
|
+
if "foreign key constraint" in msg_lower:
|
|
158
|
+
return ErrorCategory.FOREIGN_KEY_CONSTRAINT
|
|
159
|
+
if "milestone not found" in msg_lower or "no such milestone" in msg_lower:
|
|
160
|
+
return ErrorCategory.MILESTONE_NOT_FOUND
|
|
161
|
+
if "project not found" in msg_lower or "no such project" in msg_lower:
|
|
162
|
+
return ErrorCategory.PROJECT_NOT_FOUND
|
|
163
|
+
if "dependency" in msg_lower and (
|
|
164
|
+
"missing" in msg_lower or "not found" in msg_lower
|
|
165
|
+
):
|
|
166
|
+
return ErrorCategory.DEPENDENCY_MISSING
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
def _check_api_errors(self, msg_lower: str) -> ErrorCategory | None:
|
|
170
|
+
"""Check for API and network errors."""
|
|
171
|
+
if "rate limit" in msg_lower or "429" in msg_lower:
|
|
172
|
+
return ErrorCategory.API_RATE_LIMIT
|
|
173
|
+
if any(x in msg_lower for x in ["connection", "network", "unreachable"]):
|
|
174
|
+
return ErrorCategory.NETWORK_ERROR
|
|
175
|
+
if "timeout" in msg_lower or "timed out" in msg_lower:
|
|
176
|
+
return ErrorCategory.TIMEOUT
|
|
177
|
+
if "503" in msg_lower or "service unavailable" in msg_lower:
|
|
178
|
+
return ErrorCategory.SERVICE_UNAVAILABLE
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
def _check_auth_errors(self, msg_lower: str) -> ErrorCategory | None:
|
|
182
|
+
"""Check for authentication and authorization errors."""
|
|
183
|
+
if any(x in msg_lower for x in ["authentication", "unauthorized", "401"]):
|
|
184
|
+
return ErrorCategory.AUTHENTICATION_FAILED
|
|
185
|
+
if (
|
|
186
|
+
"permission denied" in msg_lower
|
|
187
|
+
or "forbidden" in msg_lower
|
|
188
|
+
or "403" in msg_lower
|
|
189
|
+
):
|
|
190
|
+
return ErrorCategory.PERMISSION_DENIED
|
|
191
|
+
if "token expired" in msg_lower or "invalid token" in msg_lower:
|
|
192
|
+
return ErrorCategory.TOKEN_EXPIRED
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def _check_data_errors(
|
|
196
|
+
self, msg_lower: str, type_lower: str
|
|
197
|
+
) -> ErrorCategory | None:
|
|
198
|
+
"""Check for data integrity errors."""
|
|
199
|
+
if "integrityerror" in type_lower or "constraint" in msg_lower:
|
|
200
|
+
return ErrorCategory.DATABASE_ERROR
|
|
201
|
+
if "validation" in msg_lower or "invalid" in msg_lower:
|
|
202
|
+
return ErrorCategory.INVALID_DATA
|
|
203
|
+
if "duplicate" in msg_lower:
|
|
204
|
+
return ErrorCategory.DUPLICATE_ENTITY
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
def _check_resource_errors(self, msg_lower: str) -> ErrorCategory | None:
|
|
208
|
+
"""Check for resource-related errors."""
|
|
209
|
+
if "deleted" in msg_lower or "removed" in msg_lower:
|
|
210
|
+
return ErrorCategory.RESOURCE_DELETED
|
|
211
|
+
if "not found" in msg_lower or "404" in msg_lower:
|
|
212
|
+
return ErrorCategory.RESOURCE_NOT_FOUND
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def _check_file_system_errors(self, type_lower: str) -> ErrorCategory | None:
|
|
216
|
+
"""Check for file system errors."""
|
|
217
|
+
if any(x in type_lower for x in ["oserror", "ioerror", "filenotfound"]):
|
|
218
|
+
return ErrorCategory.FILE_SYSTEM_ERROR
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def _get_suggested_fix(self, category: ErrorCategory, error_message: str) -> str:
|
|
222
|
+
"""Get actionable suggestion for fixing error category.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
category: Error category
|
|
226
|
+
error_message: Original error message
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Human-readable suggestion
|
|
230
|
+
"""
|
|
231
|
+
suggestions = {
|
|
232
|
+
ErrorCategory.MILESTONE_NOT_FOUND: "Run 'roadmap sync' again to pull missing milestones, or manually create the milestone locally",
|
|
233
|
+
ErrorCategory.PROJECT_NOT_FOUND: "Ensure projects are synced before issues. Check GitHub project configuration",
|
|
234
|
+
ErrorCategory.DEPENDENCY_MISSING: "Sync dependent entities first (projects → milestones → issues)",
|
|
235
|
+
ErrorCategory.FOREIGN_KEY_CONSTRAINT: "Run 'roadmap health' to check database integrity. May need to reinitialize database",
|
|
236
|
+
ErrorCategory.API_RATE_LIMIT: "Wait for rate limit to reset (usually 1 hour), or use authenticated requests",
|
|
237
|
+
ErrorCategory.NETWORK_ERROR: "Check internet connection and GitHub service status at https://githubstatus.com",
|
|
238
|
+
ErrorCategory.TIMEOUT: "Retry the operation. Consider syncing in smaller batches",
|
|
239
|
+
ErrorCategory.SERVICE_UNAVAILABLE: "GitHub is experiencing issues. Check https://githubstatus.com and retry later",
|
|
240
|
+
ErrorCategory.AUTHENTICATION_FAILED: "Run 'roadmap git setup --auth' to reconfigure GitHub token",
|
|
241
|
+
ErrorCategory.PERMISSION_DENIED: "Verify GitHub token has required permissions (repo, read:org, read:user)",
|
|
242
|
+
ErrorCategory.TOKEN_EXPIRED: "Generate new GitHub token and update with 'roadmap git setup --auth'",
|
|
243
|
+
ErrorCategory.INVALID_DATA: "Check data format. May need to update roadmap CLI to latest version",
|
|
244
|
+
ErrorCategory.SCHEMA_MISMATCH: "Run 'roadmap health fix --fix-type data_integrity' to repair schema issues",
|
|
245
|
+
ErrorCategory.DUPLICATE_ENTITY: "Run 'roadmap health fix --fix-type duplicate_issues' to resolve duplicates",
|
|
246
|
+
ErrorCategory.RESOURCE_DELETED: "Entity was deleted on remote. This is expected and safe to ignore",
|
|
247
|
+
ErrorCategory.RESOURCE_NOT_FOUND: "Entity may have been renamed or deleted. Check GitHub repository",
|
|
248
|
+
ErrorCategory.DATABASE_ERROR: "Run 'roadmap health' and 'roadmap health fix' to repair database",
|
|
249
|
+
ErrorCategory.FILE_SYSTEM_ERROR: "Check file permissions and available disk space",
|
|
250
|
+
ErrorCategory.UNKNOWN_ERROR: "Run with --verbose for more details. Consider reporting as a bug",
|
|
251
|
+
}
|
|
252
|
+
return suggestions.get(category, "Check logs for more details")
|
|
253
|
+
|
|
254
|
+
def _is_recoverable(self, category: ErrorCategory) -> bool:
|
|
255
|
+
"""Determine if error category is typically recoverable.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
category: Error category
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
True if error can typically be recovered from
|
|
262
|
+
"""
|
|
263
|
+
non_recoverable = {
|
|
264
|
+
ErrorCategory.AUTHENTICATION_FAILED,
|
|
265
|
+
ErrorCategory.TOKEN_EXPIRED,
|
|
266
|
+
ErrorCategory.PERMISSION_DENIED,
|
|
267
|
+
ErrorCategory.SCHEMA_MISMATCH,
|
|
268
|
+
}
|
|
269
|
+
return category not in non_recoverable
|
|
270
|
+
|
|
271
|
+
def get_summary(self, max_samples: int = 5) -> list[ErrorSummary]:
|
|
272
|
+
"""Get aggregated error summary by category.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
max_samples: Maximum number of sample messages/entities per category
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
List of ErrorSummary objects, sorted by count (descending)
|
|
279
|
+
"""
|
|
280
|
+
summaries = []
|
|
281
|
+
|
|
282
|
+
for category, count in self._category_counts.items():
|
|
283
|
+
# Get errors for this category
|
|
284
|
+
category_errors = [e for e in self._errors if e.category == category]
|
|
285
|
+
|
|
286
|
+
# Extract unique sample messages (top N)
|
|
287
|
+
messages = list(dict.fromkeys([e.error_message for e in category_errors]))
|
|
288
|
+
sample_messages = messages[:max_samples]
|
|
289
|
+
|
|
290
|
+
# Extract sample affected entities
|
|
291
|
+
entities = [e.entity_id for e in category_errors if e.entity_id]
|
|
292
|
+
affected_entities = list(dict.fromkeys(entities))[:max_samples]
|
|
293
|
+
|
|
294
|
+
# Get suggested fix (same for all in category)
|
|
295
|
+
suggested_fix = (
|
|
296
|
+
category_errors[0].suggested_fix
|
|
297
|
+
if category_errors and category_errors[0].suggested_fix
|
|
298
|
+
else "Check logs for more details"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Check if recoverable (should be same for all)
|
|
302
|
+
is_recoverable = (
|
|
303
|
+
category_errors[0].is_recoverable if category_errors else True
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
summary = ErrorSummary(
|
|
307
|
+
category=category,
|
|
308
|
+
count=count,
|
|
309
|
+
sample_messages=sample_messages,
|
|
310
|
+
affected_entities=affected_entities,
|
|
311
|
+
suggested_fix=suggested_fix,
|
|
312
|
+
is_recoverable=is_recoverable,
|
|
313
|
+
)
|
|
314
|
+
summaries.append(summary)
|
|
315
|
+
|
|
316
|
+
# Sort by count (most common errors first)
|
|
317
|
+
summaries.sort(key=lambda s: s.count, reverse=True)
|
|
318
|
+
return summaries
|
|
319
|
+
|
|
320
|
+
def get_total_errors(self) -> int:
|
|
321
|
+
"""Get total number of errors classified."""
|
|
322
|
+
return len(self._errors)
|
|
323
|
+
|
|
324
|
+
def get_category_count(self, category: ErrorCategory) -> int:
|
|
325
|
+
"""Get count of errors in specific category."""
|
|
326
|
+
return self._category_counts.get(category, 0)
|
|
327
|
+
|
|
328
|
+
def clear(self):
|
|
329
|
+
"""Clear all collected errors."""
|
|
330
|
+
self._errors.clear()
|
|
331
|
+
self._category_counts.clear()
|
|
332
|
+
|
|
333
|
+
def add_errors(self, errors: dict[str, str]) -> None:
|
|
334
|
+
"""Bulk add errors from a dictionary of issue_id -> error_message.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
errors: Dictionary mapping issue IDs to error messages
|
|
338
|
+
"""
|
|
339
|
+
for issue_id, error_msg in errors.items():
|
|
340
|
+
self.classify_error(
|
|
341
|
+
error_message=error_msg,
|
|
342
|
+
error_type="SyncError",
|
|
343
|
+
entity_type="Issue",
|
|
344
|
+
entity_id=issue_id,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def get_summary_dict(self) -> dict[str, int]:
|
|
348
|
+
"""Get summary as a dictionary of category group -> count.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Dict with keys like "dependency_errors", "api_errors", etc.
|
|
352
|
+
"""
|
|
353
|
+
# Group categories into high-level groups
|
|
354
|
+
category_groups = {
|
|
355
|
+
"dependency_errors": [
|
|
356
|
+
ErrorCategory.MILESTONE_NOT_FOUND,
|
|
357
|
+
ErrorCategory.PROJECT_NOT_FOUND,
|
|
358
|
+
ErrorCategory.DEPENDENCY_MISSING,
|
|
359
|
+
ErrorCategory.FOREIGN_KEY_CONSTRAINT,
|
|
360
|
+
],
|
|
361
|
+
"api_errors": [
|
|
362
|
+
ErrorCategory.API_RATE_LIMIT,
|
|
363
|
+
ErrorCategory.NETWORK_ERROR,
|
|
364
|
+
ErrorCategory.TIMEOUT,
|
|
365
|
+
ErrorCategory.SERVICE_UNAVAILABLE,
|
|
366
|
+
],
|
|
367
|
+
"auth_errors": [
|
|
368
|
+
ErrorCategory.AUTHENTICATION_FAILED,
|
|
369
|
+
ErrorCategory.PERMISSION_DENIED,
|
|
370
|
+
ErrorCategory.TOKEN_EXPIRED,
|
|
371
|
+
],
|
|
372
|
+
"data_errors": [
|
|
373
|
+
ErrorCategory.INVALID_DATA,
|
|
374
|
+
ErrorCategory.SCHEMA_MISMATCH,
|
|
375
|
+
ErrorCategory.DUPLICATE_ENTITY,
|
|
376
|
+
ErrorCategory.DATABASE_ERROR,
|
|
377
|
+
],
|
|
378
|
+
"resource_errors": [
|
|
379
|
+
ErrorCategory.RESOURCE_DELETED,
|
|
380
|
+
ErrorCategory.RESOURCE_NOT_FOUND,
|
|
381
|
+
],
|
|
382
|
+
"file_system_errors": [
|
|
383
|
+
ErrorCategory.FILE_SYSTEM_ERROR,
|
|
384
|
+
],
|
|
385
|
+
"unknown_errors": [
|
|
386
|
+
ErrorCategory.UNKNOWN_ERROR,
|
|
387
|
+
],
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# Aggregate counts by group
|
|
391
|
+
result = {}
|
|
392
|
+
for group_name, categories in category_groups.items():
|
|
393
|
+
total = sum(self._category_counts.get(cat, 0) for cat in categories)
|
|
394
|
+
result[group_name] = total
|
|
395
|
+
|
|
396
|
+
return result
|
|
397
|
+
|
|
398
|
+
def get_recommendation(self, category_group: str) -> str:
|
|
399
|
+
"""Get recommendation for a category group.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
category_group: High-level category group (e.g., "dependency_errors")
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
Recommendation string
|
|
406
|
+
"""
|
|
407
|
+
recommendations = {
|
|
408
|
+
"dependency_errors": "Ensure all dependencies (milestones, projects) are synced first. Run 'roadmap sync' again.",
|
|
409
|
+
"api_errors": "Check GitHub service status at https://githubstatus.com. Retry after a short wait.",
|
|
410
|
+
"auth_errors": "Verify GitHub token with 'roadmap git setup --auth'. Ensure token has required permissions (repo, read:org).",
|
|
411
|
+
"data_errors": "Run 'roadmap health' to check database integrity. May need to run 'roadmap health fix'.",
|
|
412
|
+
"resource_errors": "These resources may have been deleted on GitHub. This is expected after cleanup.",
|
|
413
|
+
"file_system_errors": "Check file permissions and disk space. Ensure .roadmap/ directory is writable.",
|
|
414
|
+
"unknown_errors": "Run with --verbose for detailed error messages. Consider reporting as a bug if persistent.",
|
|
415
|
+
}
|
|
416
|
+
return recommendations.get(category_group, "Check logs for more details")
|
|
417
|
+
|
|
418
|
+
def get_issues_by_category(self, category_group: str) -> list[str]:
|
|
419
|
+
"""Get list of issue IDs affected by a category group.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
category_group: High-level category group (e.g., "dependency_errors")
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
List of issue IDs
|
|
426
|
+
"""
|
|
427
|
+
# Map group to categories
|
|
428
|
+
category_groups = {
|
|
429
|
+
"dependency_errors": [
|
|
430
|
+
ErrorCategory.MILESTONE_NOT_FOUND,
|
|
431
|
+
ErrorCategory.PROJECT_NOT_FOUND,
|
|
432
|
+
ErrorCategory.DEPENDENCY_MISSING,
|
|
433
|
+
ErrorCategory.FOREIGN_KEY_CONSTRAINT,
|
|
434
|
+
],
|
|
435
|
+
"api_errors": [
|
|
436
|
+
ErrorCategory.API_RATE_LIMIT,
|
|
437
|
+
ErrorCategory.NETWORK_ERROR,
|
|
438
|
+
ErrorCategory.TIMEOUT,
|
|
439
|
+
ErrorCategory.SERVICE_UNAVAILABLE,
|
|
440
|
+
],
|
|
441
|
+
"auth_errors": [
|
|
442
|
+
ErrorCategory.AUTHENTICATION_FAILED,
|
|
443
|
+
ErrorCategory.PERMISSION_DENIED,
|
|
444
|
+
ErrorCategory.TOKEN_EXPIRED,
|
|
445
|
+
],
|
|
446
|
+
"data_errors": [
|
|
447
|
+
ErrorCategory.INVALID_DATA,
|
|
448
|
+
ErrorCategory.SCHEMA_MISMATCH,
|
|
449
|
+
ErrorCategory.DUPLICATE_ENTITY,
|
|
450
|
+
ErrorCategory.DATABASE_ERROR,
|
|
451
|
+
],
|
|
452
|
+
"resource_errors": [
|
|
453
|
+
ErrorCategory.RESOURCE_DELETED,
|
|
454
|
+
ErrorCategory.RESOURCE_NOT_FOUND,
|
|
455
|
+
],
|
|
456
|
+
"file_system_errors": [
|
|
457
|
+
ErrorCategory.FILE_SYSTEM_ERROR,
|
|
458
|
+
],
|
|
459
|
+
"unknown_errors": [
|
|
460
|
+
ErrorCategory.UNKNOWN_ERROR,
|
|
461
|
+
],
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
categories = category_groups.get(category_group, [])
|
|
465
|
+
issue_ids = [
|
|
466
|
+
e.entity_id
|
|
467
|
+
for e in self._errors
|
|
468
|
+
if e.category in categories and e.entity_id
|
|
469
|
+
]
|
|
470
|
+
return issue_ids
|