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,119 @@
|
|
|
1
|
+
"""Specialized adapter interfaces for sync services.
|
|
2
|
+
|
|
3
|
+
Defines contracts for sync-specific operations without layer violations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SyncLinkingServiceInterface(ABC):
|
|
11
|
+
"""Contract for sync linking operations.
|
|
12
|
+
|
|
13
|
+
Handles linking/unlinking of local and remote issues during sync.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def link_issues(self, local_id: str, remote_id: str) -> bool: # noqa: F841
|
|
18
|
+
"""Link a local issue to a remote issue.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
local_id: Local issue ID
|
|
22
|
+
remote_id: Remote issue ID
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
True if successful
|
|
26
|
+
"""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def unlink_issue(self, local_id: str) -> bool: # noqa: F841
|
|
31
|
+
"""Unlink an issue from its remote mapping.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
local_id: Local issue ID
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
True if successful
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def get_linked_remote_id(self, local_id: str) -> str | None: # noqa: F841
|
|
43
|
+
"""Get the remote issue ID for a local issue.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
local_id: Local issue ID
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Remote issue ID or None if not linked
|
|
50
|
+
"""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def get_linked_local_id(self, remote_id: str) -> str | None:
|
|
55
|
+
"""Get the local issue ID for a remote issue.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
remote_id: Remote issue ID
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Local issue ID or None if not linked
|
|
62
|
+
"""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SyncCacheServiceInterface(ABC):
|
|
67
|
+
"""Contract for sync caching operations.
|
|
68
|
+
|
|
69
|
+
Manages cache of remote state to reduce API calls.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def cache_remote_issue(self, issue_id: str, issue_data: dict[str, Any]) -> bool:
|
|
74
|
+
"""Cache remote issue data.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
issue_id: Issue identifier
|
|
78
|
+
issue_data: Issue data dictionary
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
True if successful
|
|
82
|
+
"""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def get_cached_remote_issue(self, issue_id: str) -> dict[str, Any] | None:
|
|
87
|
+
"""Get cached remote issue data.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
issue_id: Issue identifier
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Issue data or None if not cached
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def invalidate_cache(self, issue_id: str | None = None) -> bool:
|
|
99
|
+
"""Invalidate cache.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
issue_id: Specific issue ID to invalidate, or None for all
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
True if successful
|
|
106
|
+
"""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def is_cache_fresh(self, issue_id: str) -> bool:
|
|
111
|
+
"""Check if cached data is still fresh.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
issue_id: Issue identifier
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True if cache is valid and fresh
|
|
118
|
+
"""
|
|
119
|
+
pass
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Protocol and abstract base class definitions for sync validation.
|
|
2
|
+
|
|
3
|
+
This module defines validators to ensure data integrity and prevent database
|
|
4
|
+
corruption during sync operations, particularly foreign key constraint violations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Protocol
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ForeignKeyValidator(ABC):
|
|
12
|
+
"""Abstract base class for validating foreign key constraints before syncing.
|
|
13
|
+
|
|
14
|
+
Ensures prerequisite data exists before syncing dependent entities,
|
|
15
|
+
preventing FOREIGN KEY constraint violations.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
validator = MilestoneFKValidator(db_connection)
|
|
19
|
+
try:
|
|
20
|
+
validator.validate() # Raises if projects table is empty
|
|
21
|
+
except ForeignKeyValidationError as e:
|
|
22
|
+
logger.error(f"Validation failed: {e}")
|
|
23
|
+
raise
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def validate(self) -> None:
|
|
28
|
+
"""Validate that all foreign key prerequisites exist.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
ForeignKeyValidationError: If prerequisite data doesn't exist
|
|
32
|
+
RuntimeError: If database connection fails
|
|
33
|
+
|
|
34
|
+
Notes:
|
|
35
|
+
- Should fail fast on missing prerequisites
|
|
36
|
+
- Must not swallow exceptions
|
|
37
|
+
- Log specific missing references with entity IDs
|
|
38
|
+
"""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def missing_prerequisites(self) -> list[str]:
|
|
43
|
+
"""Return list of missing prerequisite entities.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of entity IDs or descriptions that are missing
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
['project-id-1', 'project-id-2']
|
|
50
|
+
"""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SyncPhaseOrder(Protocol):
|
|
55
|
+
"""Protocol defining ordered sync phases to prevent constraint violations.
|
|
56
|
+
|
|
57
|
+
Ensures entities are synced in correct dependency order:
|
|
58
|
+
1. Projects (no dependencies)
|
|
59
|
+
2. Milestones (depends on projects)
|
|
60
|
+
3. Issues (depends on projects and milestones)
|
|
61
|
+
|
|
62
|
+
Implementation should enforce this ordering and fail if called out-of-order.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def validate_phase_order(self, _current_phase: str, _next_phase: str) -> bool:
|
|
66
|
+
"""Validate that phase transition is allowed.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
current_phase: Current sync phase ('projects', 'milestones', 'issues')
|
|
70
|
+
next_phase: Next sync phase to execute
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if transition is valid, False otherwise
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If phases are invalid or transition violates ordering
|
|
77
|
+
"""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def get_phase_order(self) -> list[str]:
|
|
81
|
+
"""Get the canonical phase execution order.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Ordered list of phases: ['projects', 'milestones', 'issues']
|
|
85
|
+
"""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class PreSyncValidator(Protocol):
|
|
90
|
+
"""Protocol for pre-sync validation with fail-fast semantics.
|
|
91
|
+
|
|
92
|
+
Validates preconditions before any sync operations begin.
|
|
93
|
+
Failures immediately abort sync to prevent partial state corruptions.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def validate_pre_sync(self, _roadmap_dir: str | None = None) -> dict[str, Any]:
|
|
97
|
+
"""Validate preconditions before sync.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
roadmap_dir: Optional roadmap directory path to validate
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
dict with validation results:
|
|
104
|
+
{
|
|
105
|
+
'valid': bool,
|
|
106
|
+
'errors': list[str],
|
|
107
|
+
'warnings': list[str],
|
|
108
|
+
'prerequisite_count': int
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValueError: If critical preconditions fail (fail-fast)
|
|
113
|
+
RuntimeError: If database connection fails
|
|
114
|
+
|
|
115
|
+
Notes:
|
|
116
|
+
- Errors (critical) cause immediate abort via exception
|
|
117
|
+
- Warnings (non-critical) logged but allow sync to proceed
|
|
118
|
+
- Should check prerequisite tables before FK-dependent syncing
|
|
119
|
+
"""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class AtomicSyncPhase(Protocol):
|
|
124
|
+
"""Protocol for atomic sync operations (transaction-based).
|
|
125
|
+
|
|
126
|
+
Wraps entity sync in transactions to ensure all-or-nothing semantics
|
|
127
|
+
per sync phase.
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
with atomic_sync('projects') as phase:
|
|
131
|
+
sync_project_1() # If any sync fails, entire phase rolls back
|
|
132
|
+
sync_project_2()
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __enter__(self) -> "AtomicSyncPhase":
|
|
136
|
+
"""Enter atomic sync context (start transaction)."""
|
|
137
|
+
...
|
|
138
|
+
|
|
139
|
+
def __exit__(
|
|
140
|
+
self,
|
|
141
|
+
_exc_type: type,
|
|
142
|
+
_exc_val: Exception,
|
|
143
|
+
_exc_tb: Any,
|
|
144
|
+
) -> None:
|
|
145
|
+
"""Exit atomic sync context (commit or rollback)."""
|
|
146
|
+
...
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ForeignKeyValidationError(Exception):
|
|
150
|
+
"""Raised when foreign key validation fails.
|
|
151
|
+
|
|
152
|
+
Indicates missing prerequisite data that would cause FK constraint
|
|
153
|
+
violations if sync proceeded.
|
|
154
|
+
|
|
155
|
+
Attributes:
|
|
156
|
+
entity_type: Type of entity with missing FK (e.g., 'milestone')
|
|
157
|
+
missing_references: List of missing referenced entity IDs
|
|
158
|
+
error_details: Detailed error message with context
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(
|
|
162
|
+
self,
|
|
163
|
+
entity_type: str,
|
|
164
|
+
missing_references: list[str],
|
|
165
|
+
error_details: str,
|
|
166
|
+
):
|
|
167
|
+
"""Initialize validation error.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
entity_type: Type of entity that failed validation
|
|
171
|
+
missing_references: IDs of missing prerequisite entities
|
|
172
|
+
error_details: Detailed error context
|
|
173
|
+
"""
|
|
174
|
+
self.entity_type = entity_type
|
|
175
|
+
self.missing_references = missing_references
|
|
176
|
+
self.error_details = error_details
|
|
177
|
+
super().__init__(
|
|
178
|
+
f"FK validation failed for {entity_type}: "
|
|
179
|
+
f"missing {len(missing_references)} references. {error_details}"
|
|
180
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Core models for roadmap."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from roadmap.core.services.sync.sync_state import IssueBaseState, SyncState
|
|
6
|
+
|
|
7
|
+
# Import from parent models.py (service params and other models)
|
|
8
|
+
# Get the parent directory's models.py file
|
|
9
|
+
_models_file = Path(__file__).parent.parent / "models.py"
|
|
10
|
+
_models_spec = __import__("importlib.util").util.spec_from_file_location(
|
|
11
|
+
"_models_module", _models_file
|
|
12
|
+
)
|
|
13
|
+
_models_mod = __import__("importlib.util").util.module_from_spec(_models_spec)
|
|
14
|
+
_models_spec.loader.exec_module(_models_mod)
|
|
15
|
+
|
|
16
|
+
# Re-export items from models.py
|
|
17
|
+
NOT_PROVIDED = _models_mod.NOT_PROVIDED
|
|
18
|
+
IssueCreateServiceParams = _models_mod.IssueCreateServiceParams
|
|
19
|
+
IssueUpdateServiceParams = _models_mod.IssueUpdateServiceParams
|
|
20
|
+
ProjectCreateServiceParams = _models_mod.ProjectCreateServiceParams
|
|
21
|
+
ProjectUpdateServiceParams = _models_mod.ProjectUpdateServiceParams
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"NOT_PROVIDED",
|
|
25
|
+
"IssueCreateServiceParams",
|
|
26
|
+
"IssueUpdateServiceParams",
|
|
27
|
+
"ProjectCreateServiceParams",
|
|
28
|
+
"ProjectUpdateServiceParams",
|
|
29
|
+
"IssueBaseState",
|
|
30
|
+
"SyncState",
|
|
31
|
+
]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Sync dataclasses - canonical representations for sync operations.
|
|
2
|
+
|
|
3
|
+
These dataclasses serve as the common currency between local state and remote state
|
|
4
|
+
during sync operations. Backends normalize their API responses into these types,
|
|
5
|
+
and the generic orchestrator works exclusively with them.
|
|
6
|
+
|
|
7
|
+
This ensures the core sync logic is completely backend-agnostic.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import asdict, dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class SyncIssue:
|
|
17
|
+
"""Canonical representation of an issue for sync operations.
|
|
18
|
+
|
|
19
|
+
Used as the common currency between local and remote states.
|
|
20
|
+
Backends normalize their API responses into this format.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Core fields (present in all backends)
|
|
24
|
+
id: str
|
|
25
|
+
title: str
|
|
26
|
+
status: str # Normalized: "open", "closed", "in_progress", etc.
|
|
27
|
+
|
|
28
|
+
# Optional fields (may not be present in all backends)
|
|
29
|
+
headline: str = ""
|
|
30
|
+
assignee: str | None = None
|
|
31
|
+
milestone: str | None = None
|
|
32
|
+
labels: list[str] = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
# Timing
|
|
35
|
+
created_at: datetime | None = None
|
|
36
|
+
updated_at: datetime | None = None
|
|
37
|
+
|
|
38
|
+
# Backend tracking
|
|
39
|
+
backend_name: str = "unknown" # "github", "gitlab", "git", etc.
|
|
40
|
+
backend_id: str | int | None = None # Native backend ID
|
|
41
|
+
remote_ids: dict[str, str | int] = field(
|
|
42
|
+
default_factory=dict
|
|
43
|
+
) # All known remote IDs
|
|
44
|
+
|
|
45
|
+
# Extensibility for backend-specific data
|
|
46
|
+
custom_fields: dict[str, Any] = field(
|
|
47
|
+
default_factory=dict
|
|
48
|
+
) # Jira-style custom fields
|
|
49
|
+
raw_response: dict[str, Any] = field(default_factory=dict) # Raw API response
|
|
50
|
+
metadata: dict[str, Any] = field(default_factory=dict) # Custom tracking data
|
|
51
|
+
|
|
52
|
+
def __post_init__(self):
|
|
53
|
+
"""Validate sync issue data."""
|
|
54
|
+
if not self.id:
|
|
55
|
+
raise ValueError("SyncIssue.id is required")
|
|
56
|
+
if not self.title:
|
|
57
|
+
raise ValueError("SyncIssue.title is required")
|
|
58
|
+
if not self.status:
|
|
59
|
+
raise ValueError("SyncIssue.status is required")
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> dict[str, Any]:
|
|
62
|
+
"""Convert to dictionary for serialization."""
|
|
63
|
+
return asdict(self)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_dict(cls, data: dict[str, Any]) -> "SyncIssue":
|
|
67
|
+
"""Create from dictionary (e.g., from JSON/YAML)."""
|
|
68
|
+
# Handle datetime strings
|
|
69
|
+
if isinstance(data.get("created_at"), str):
|
|
70
|
+
data["created_at"] = datetime.fromisoformat(data["created_at"])
|
|
71
|
+
if isinstance(data.get("updated_at"), str):
|
|
72
|
+
data["updated_at"] = datetime.fromisoformat(data["updated_at"])
|
|
73
|
+
|
|
74
|
+
# Filter to only known fields
|
|
75
|
+
valid_fields = {f.name for f in cls.__dataclass_fields__.values()}
|
|
76
|
+
filtered_data = {k: v for k, v in data.items() if k in valid_fields}
|
|
77
|
+
return cls(**filtered_data)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class SyncMilestone:
|
|
82
|
+
"""Canonical representation of a milestone for sync operations."""
|
|
83
|
+
|
|
84
|
+
# Core fields
|
|
85
|
+
id: str
|
|
86
|
+
name: str
|
|
87
|
+
status: str = "open" # "open", "closed"
|
|
88
|
+
|
|
89
|
+
# Optional fields
|
|
90
|
+
headline: str | None = None
|
|
91
|
+
due_date: datetime | None = None
|
|
92
|
+
|
|
93
|
+
# Backend tracking
|
|
94
|
+
backend_name: str = "unknown"
|
|
95
|
+
backend_id: str | int | None = None
|
|
96
|
+
remote_ids: dict[str, str | int] = field(default_factory=dict)
|
|
97
|
+
|
|
98
|
+
# Extensibility
|
|
99
|
+
custom_fields: dict[str, Any] = field(default_factory=dict)
|
|
100
|
+
raw_response: dict[str, Any] = field(default_factory=dict)
|
|
101
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
102
|
+
|
|
103
|
+
def __post_init__(self):
|
|
104
|
+
"""Validate sync milestone data."""
|
|
105
|
+
if not self.id:
|
|
106
|
+
raise ValueError("SyncMilestone.id is required")
|
|
107
|
+
if not self.name:
|
|
108
|
+
raise ValueError("SyncMilestone.name is required")
|
|
109
|
+
|
|
110
|
+
def to_dict(self) -> dict[str, Any]:
|
|
111
|
+
"""Convert to dictionary for serialization."""
|
|
112
|
+
return asdict(self)
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def from_dict(cls, data: dict[str, Any]) -> "SyncMilestone":
|
|
116
|
+
"""Create from dictionary."""
|
|
117
|
+
if isinstance(data.get("due_date"), str):
|
|
118
|
+
data["due_date"] = datetime.fromisoformat(data["due_date"])
|
|
119
|
+
|
|
120
|
+
valid_fields = {f.name for f in cls.__dataclass_fields__.values()}
|
|
121
|
+
filtered_data = {k: v for k, v in data.items() if k in valid_fields}
|
|
122
|
+
return cls(**filtered_data)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class SyncProject:
|
|
127
|
+
"""Canonical representation of a project for sync operations."""
|
|
128
|
+
|
|
129
|
+
# Core fields
|
|
130
|
+
id: str
|
|
131
|
+
name: str
|
|
132
|
+
|
|
133
|
+
# Optional fields
|
|
134
|
+
headline: str | None = None
|
|
135
|
+
|
|
136
|
+
# Backend tracking
|
|
137
|
+
backend_name: str = "unknown"
|
|
138
|
+
backend_id: str | int | None = None
|
|
139
|
+
remote_ids: dict[str, str | int] = field(default_factory=dict)
|
|
140
|
+
|
|
141
|
+
# Extensibility
|
|
142
|
+
custom_fields: dict[str, Any] = field(default_factory=dict)
|
|
143
|
+
raw_response: dict[str, Any] = field(default_factory=dict)
|
|
144
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
145
|
+
|
|
146
|
+
def __post_init__(self):
|
|
147
|
+
"""Validate sync project data."""
|
|
148
|
+
if not self.id:
|
|
149
|
+
raise ValueError("SyncProject.id is required")
|
|
150
|
+
if not self.name:
|
|
151
|
+
raise ValueError("SyncProject.name is required")
|
|
152
|
+
|
|
153
|
+
def to_dict(self) -> dict[str, Any]:
|
|
154
|
+
"""Convert to dictionary for serialization."""
|
|
155
|
+
return asdict(self)
|
|
156
|
+
|
|
157
|
+
@classmethod
|
|
158
|
+
def from_dict(cls, data: dict[str, Any]) -> "SyncProject":
|
|
159
|
+
"""Create from dictionary."""
|
|
160
|
+
valid_fields = {f.name for f in cls.__dataclass_fields__.values()}
|
|
161
|
+
filtered_data = {k: v for k, v in data.items() if k in valid_fields}
|
|
162
|
+
return cls(**filtered_data)
|
roadmap/core/models.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Service layer dataclasses for internal parameter passing.
|
|
2
|
+
|
|
3
|
+
These models decouple service method signatures from their implementations.
|
|
4
|
+
They're used internally by services (not exposed to CLI).
|
|
5
|
+
|
|
6
|
+
Benefits:
|
|
7
|
+
- Services have clean, stable signatures
|
|
8
|
+
- Easy to add new fields without breaking callers
|
|
9
|
+
- Self-documenting business logic
|
|
10
|
+
- Better testability (can mock as single object)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Sentinel value to distinguish "not provided" from "None"
|
|
18
|
+
class _NotProvided:
|
|
19
|
+
"""Sentinel value indicating a field was not provided in an update."""
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
return "<NOT_PROVIDED>"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
NOT_PROVIDED = _NotProvided()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class IssueCreateServiceParams:
|
|
30
|
+
"""Parameters for creating an issue (service layer)."""
|
|
31
|
+
|
|
32
|
+
title: str
|
|
33
|
+
priority: str | None = None
|
|
34
|
+
status: str = "todo"
|
|
35
|
+
issue_type: str | None = None
|
|
36
|
+
milestone: str | None = None
|
|
37
|
+
assignee: str | None = None
|
|
38
|
+
content: str | None = None
|
|
39
|
+
estimate: float | None = None
|
|
40
|
+
labels: list[str] = field(default_factory=list)
|
|
41
|
+
depends_on: list[str] = field(default_factory=list)
|
|
42
|
+
blocks: list[str] = field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class IssueUpdateServiceParams:
|
|
47
|
+
"""Parameters for updating an issue (service layer)."""
|
|
48
|
+
|
|
49
|
+
issue_id: str
|
|
50
|
+
title: str | None | _NotProvided = NOT_PROVIDED
|
|
51
|
+
priority: str | None | _NotProvided = NOT_PROVIDED
|
|
52
|
+
status: str | None | _NotProvided = NOT_PROVIDED
|
|
53
|
+
assignee: str | None | _NotProvided = NOT_PROVIDED
|
|
54
|
+
milestone: str | None | _NotProvided = NOT_PROVIDED
|
|
55
|
+
content: str | None | _NotProvided = NOT_PROVIDED
|
|
56
|
+
estimate: float | None | _NotProvided = NOT_PROVIDED
|
|
57
|
+
reason: str | None | _NotProvided = NOT_PROVIDED
|
|
58
|
+
archived: bool | _NotProvided = NOT_PROVIDED
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class IssueQueryParams:
|
|
63
|
+
"""Parameters for querying/filtering issues (service layer)."""
|
|
64
|
+
|
|
65
|
+
status: str | None = None
|
|
66
|
+
priority: str | None = None
|
|
67
|
+
issue_type: str | None = None
|
|
68
|
+
assignee: str | None = None
|
|
69
|
+
milestone: str | None = None
|
|
70
|
+
overdue: bool = False
|
|
71
|
+
blocked: bool = False
|
|
72
|
+
unassigned: bool = False
|
|
73
|
+
limit: int | None = None
|
|
74
|
+
offset: int = 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class MilestoneCreateServiceParams:
|
|
79
|
+
"""Parameters for creating a milestone (service layer)."""
|
|
80
|
+
|
|
81
|
+
name: str
|
|
82
|
+
content: str = ""
|
|
83
|
+
due_date: datetime | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class MilestoneUpdateServiceParams:
|
|
88
|
+
"""Parameters for updating a milestone (service layer)."""
|
|
89
|
+
|
|
90
|
+
name: str
|
|
91
|
+
content: str | None = None
|
|
92
|
+
due_date: datetime | None = None
|
|
93
|
+
clear_due_date: bool = False
|
|
94
|
+
status: str | None = None
|
|
95
|
+
archived: bool | _NotProvided = NOT_PROVIDED
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class ProjectCreateServiceParams:
|
|
100
|
+
"""Parameters for creating a project (service layer)."""
|
|
101
|
+
|
|
102
|
+
name: str
|
|
103
|
+
content: str = ""
|
|
104
|
+
milestones: list | None = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class ProjectUpdateServiceParams:
|
|
109
|
+
"""Parameters for updating a project (service layer)."""
|
|
110
|
+
|
|
111
|
+
project_id: str
|
|
112
|
+
name: str | None = None
|
|
113
|
+
content: str | None = None
|
|
114
|
+
milestones: list | None = None
|
|
115
|
+
status: str | None = None
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Observability and metrics tracking for sync operations.
|
|
2
|
+
|
|
3
|
+
Provides comprehensive metrics collection, aggregation, and reporting
|
|
4
|
+
for sync operations including:
|
|
5
|
+
- Deduplication metrics (local/remote reduction percentages, timing)
|
|
6
|
+
- Fetch/push/pull operation metrics
|
|
7
|
+
- Conflict detection and resolution tracking
|
|
8
|
+
- Duplicate detection and resolution metrics
|
|
9
|
+
- Performance timing and throughput
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .sync_metrics import SyncMetrics, SyncObservability
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"SyncMetrics",
|
|
16
|
+
"SyncObservability",
|
|
17
|
+
]
|