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.
Files changed (521) hide show
  1. roadmap/__init__.py +27 -0
  2. roadmap/adapters/__init__.py +1 -0
  3. roadmap/adapters/base_paginated_adapter.py +117 -0
  4. roadmap/adapters/cli/__init__.py +249 -0
  5. roadmap/adapters/cli/analysis/__init__.py +5 -0
  6. roadmap/adapters/cli/analysis/commands.py +370 -0
  7. roadmap/adapters/cli/analysis/presenter.py +125 -0
  8. roadmap/adapters/cli/archive_operations.py +86 -0
  9. roadmap/adapters/cli/cli_command_helpers.py +109 -0
  10. roadmap/adapters/cli/cli_confirmations.py +74 -0
  11. roadmap/adapters/cli/cli_error_handlers.py +146 -0
  12. roadmap/adapters/cli/cli_validators.py +39 -0
  13. roadmap/adapters/cli/commands/sync_status.py +308 -0
  14. roadmap/adapters/cli/comment/__init__.py +5 -0
  15. roadmap/adapters/cli/comment/commands.py +106 -0
  16. roadmap/adapters/cli/config/__init__.py +5 -0
  17. roadmap/adapters/cli/config/commands.py +198 -0
  18. roadmap/adapters/cli/console_exports.py +10 -0
  19. roadmap/adapters/cli/core.py +26 -0
  20. roadmap/adapters/cli/crud/__init__.py +47 -0
  21. roadmap/adapters/cli/crud/base_archive.py +208 -0
  22. roadmap/adapters/cli/crud/base_create.py +154 -0
  23. roadmap/adapters/cli/crud/base_delete.py +155 -0
  24. roadmap/adapters/cli/crud/base_restore.py +283 -0
  25. roadmap/adapters/cli/crud/base_update.py +193 -0
  26. roadmap/adapters/cli/crud/crud_helpers.py +206 -0
  27. roadmap/adapters/cli/crud/crud_utils.py +152 -0
  28. roadmap/adapters/cli/crud/entity_builders.py +374 -0
  29. roadmap/adapters/cli/data/__init__.py +8 -0
  30. roadmap/adapters/cli/data/commands.py +149 -0
  31. roadmap/adapters/cli/decorators.py +208 -0
  32. roadmap/adapters/cli/dtos/__init__.py +155 -0
  33. roadmap/adapters/cli/exception_handler.py +105 -0
  34. roadmap/adapters/cli/git/__init__.py +8 -0
  35. roadmap/adapters/cli/git/commands.py +475 -0
  36. roadmap/adapters/cli/git/handlers/__init__.py +1 -0
  37. roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
  38. roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
  39. roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
  40. roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
  41. roadmap/adapters/cli/git/hooks_config.py +327 -0
  42. roadmap/adapters/cli/git/status_display.py +158 -0
  43. roadmap/adapters/cli/health/__init__.py +8 -0
  44. roadmap/adapters/cli/health/db_integrity.py +204 -0
  45. roadmap/adapters/cli/health/enhancer.py +227 -0
  46. roadmap/adapters/cli/health/fixer.py +295 -0
  47. roadmap/adapters/cli/health/fixers/__init__.py +10 -0
  48. roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
  49. roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
  50. roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
  51. roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
  52. roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
  53. roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
  54. roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
  55. roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
  56. roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
  57. roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
  58. roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
  59. roadmap/adapters/cli/health/formatter.py +250 -0
  60. roadmap/adapters/cli/health/formatters.py +427 -0
  61. roadmap/adapters/cli/health/scan.py +293 -0
  62. roadmap/adapters/cli/init/__init__.py +5 -0
  63. roadmap/adapters/cli/init/commands.py +587 -0
  64. roadmap/adapters/cli/init.py +9 -0
  65. roadmap/adapters/cli/issues/__init__.py +70 -0
  66. roadmap/adapters/cli/issues/archive.py +137 -0
  67. roadmap/adapters/cli/issues/archive_class.py +253 -0
  68. roadmap/adapters/cli/issues/block.py +39 -0
  69. roadmap/adapters/cli/issues/close.py +216 -0
  70. roadmap/adapters/cli/issues/comment.py +178 -0
  71. roadmap/adapters/cli/issues/create.py +162 -0
  72. roadmap/adapters/cli/issues/delete.py +31 -0
  73. roadmap/adapters/cli/issues/deps.py +161 -0
  74. roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
  75. roadmap/adapters/cli/issues/link.py +308 -0
  76. roadmap/adapters/cli/issues/list.py +402 -0
  77. roadmap/adapters/cli/issues/lookup.py +96 -0
  78. roadmap/adapters/cli/issues/progress.py +102 -0
  79. roadmap/adapters/cli/issues/restore.py +96 -0
  80. roadmap/adapters/cli/issues/restore_class.py +57 -0
  81. roadmap/adapters/cli/issues/start.py +165 -0
  82. roadmap/adapters/cli/issues/sync_status.py +374 -0
  83. roadmap/adapters/cli/issues/unblock.py +43 -0
  84. roadmap/adapters/cli/issues/unlink.py +104 -0
  85. roadmap/adapters/cli/issues/update.py +120 -0
  86. roadmap/adapters/cli/issues/view.py +36 -0
  87. roadmap/adapters/cli/layout.py +220 -0
  88. roadmap/adapters/cli/mappers.py +200 -0
  89. roadmap/adapters/cli/milestones/__init__.py +52 -0
  90. roadmap/adapters/cli/milestones/archive.py +125 -0
  91. roadmap/adapters/cli/milestones/archive_class.py +236 -0
  92. roadmap/adapters/cli/milestones/assign.py +62 -0
  93. roadmap/adapters/cli/milestones/close.py +177 -0
  94. roadmap/adapters/cli/milestones/create.py +131 -0
  95. roadmap/adapters/cli/milestones/delete.py +27 -0
  96. roadmap/adapters/cli/milestones/kanban.py +123 -0
  97. roadmap/adapters/cli/milestones/list.py +87 -0
  98. roadmap/adapters/cli/milestones/recalculate.py +79 -0
  99. roadmap/adapters/cli/milestones/restore.py +87 -0
  100. roadmap/adapters/cli/milestones/restore_class.py +99 -0
  101. roadmap/adapters/cli/milestones/update.py +61 -0
  102. roadmap/adapters/cli/milestones/view.py +142 -0
  103. roadmap/adapters/cli/output_manager.py +319 -0
  104. roadmap/adapters/cli/presentation/base_presenter.py +121 -0
  105. roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
  106. roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
  107. roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
  108. roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
  109. roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
  110. roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
  111. roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
  112. roadmap/adapters/cli/presentation/project_presenter.py +233 -0
  113. roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
  114. roadmap/adapters/cli/presentation/table_builders.py +95 -0
  115. roadmap/adapters/cli/projects/__init__.py +42 -0
  116. roadmap/adapters/cli/projects/archive.py +127 -0
  117. roadmap/adapters/cli/projects/archive_class.py +96 -0
  118. roadmap/adapters/cli/projects/close.py +94 -0
  119. roadmap/adapters/cli/projects/create.py +50 -0
  120. roadmap/adapters/cli/projects/delete.py +27 -0
  121. roadmap/adapters/cli/projects/list.py +227 -0
  122. roadmap/adapters/cli/projects/restore.py +87 -0
  123. roadmap/adapters/cli/projects/restore_class.py +56 -0
  124. roadmap/adapters/cli/projects/update.py +57 -0
  125. roadmap/adapters/cli/projects/view.py +157 -0
  126. roadmap/adapters/cli/services/__init__.py +1 -0
  127. roadmap/adapters/cli/services/daily_summary_service.py +313 -0
  128. roadmap/adapters/cli/services/export_manager.py +268 -0
  129. roadmap/adapters/cli/services/milestone_list_service.py +266 -0
  130. roadmap/adapters/cli/services/project_initialization_service.py +29 -0
  131. roadmap/adapters/cli/services/project_status_service.py +462 -0
  132. roadmap/adapters/cli/services/sync_service.py +62 -0
  133. roadmap/adapters/cli/status.py +408 -0
  134. roadmap/adapters/cli/styling.py +21 -0
  135. roadmap/adapters/cli/sync.py +1117 -0
  136. roadmap/adapters/cli/sync_context.py +432 -0
  137. roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
  138. roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
  139. roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
  140. roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
  141. roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
  142. roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
  143. roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
  144. roadmap/adapters/cli/sync_metrics_command.py +190 -0
  145. roadmap/adapters/cli/sync_presenter.py +58 -0
  146. roadmap/adapters/cli/sync_validation.py +295 -0
  147. roadmap/adapters/cli/today.py +52 -0
  148. roadmap/adapters/cli/utils/click_options.py +38 -0
  149. roadmap/adapters/git/__init__.py +1 -0
  150. roadmap/adapters/git/git.py +208 -0
  151. roadmap/adapters/git/git_branch_manager.py +400 -0
  152. roadmap/adapters/git/git_command_executor.py +56 -0
  153. roadmap/adapters/git/git_commit_analyzer.py +334 -0
  154. roadmap/adapters/git/git_hooks.py +22 -0
  155. roadmap/adapters/git/git_hooks_manager.py +455 -0
  156. roadmap/adapters/git/git_repository_info.py +65 -0
  157. roadmap/adapters/git/hook_installer.py +106 -0
  158. roadmap/adapters/git/hook_registry.py +97 -0
  159. roadmap/adapters/git/hook_script_generator.py +58 -0
  160. roadmap/adapters/git/sync_monitor.py +409 -0
  161. roadmap/adapters/git/workflow_automation.py +286 -0
  162. roadmap/adapters/github/__init__.py +1 -0
  163. roadmap/adapters/github/github.py +360 -0
  164. roadmap/adapters/github/handlers/__init__.py +17 -0
  165. roadmap/adapters/github/handlers/base.py +337 -0
  166. roadmap/adapters/github/handlers/collaborators.py +165 -0
  167. roadmap/adapters/github/handlers/comments.py +118 -0
  168. roadmap/adapters/github/handlers/issues.py +150 -0
  169. roadmap/adapters/github/handlers/labels.py +164 -0
  170. roadmap/adapters/github/handlers/milestones.py +104 -0
  171. roadmap/adapters/persistence/__init__.py +35 -0
  172. roadmap/adapters/persistence/conflict_resolver.py +190 -0
  173. roadmap/adapters/persistence/database_manager.py +492 -0
  174. roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
  175. roadmap/adapters/persistence/file_locking.py +348 -0
  176. roadmap/adapters/persistence/file_parser.py +92 -0
  177. roadmap/adapters/persistence/file_synchronizer.py +107 -0
  178. roadmap/adapters/persistence/focused_managers.py +140 -0
  179. roadmap/adapters/persistence/git_history.py +330 -0
  180. roadmap/adapters/persistence/parser/__init__.py +20 -0
  181. roadmap/adapters/persistence/parser/frontmatter.py +152 -0
  182. roadmap/adapters/persistence/parser/issue.py +245 -0
  183. roadmap/adapters/persistence/parser/milestone.py +146 -0
  184. roadmap/adapters/persistence/parser/project.py +106 -0
  185. roadmap/adapters/persistence/persistence.py +288 -0
  186. roadmap/adapters/persistence/repositories/__init__.py +15 -0
  187. roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
  188. roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
  189. roadmap/adapters/persistence/repositories/project_repository.py +168 -0
  190. roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
  191. roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
  192. roadmap/adapters/persistence/storage/__init__.py +36 -0
  193. roadmap/adapters/persistence/storage/conflicts.py +101 -0
  194. roadmap/adapters/persistence/storage/connection_manager.py +79 -0
  195. roadmap/adapters/persistence/storage/issue_storage.py +89 -0
  196. roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
  197. roadmap/adapters/persistence/storage/project_storage.py +102 -0
  198. roadmap/adapters/persistence/storage/queries.py +227 -0
  199. roadmap/adapters/persistence/storage/state_manager.py +652 -0
  200. roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
  201. roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
  202. roadmap/adapters/persistence/sync_orchestrator.py +293 -0
  203. roadmap/adapters/persistence/sync_state_tracker.py +86 -0
  204. roadmap/adapters/persistence/yaml_repositories.py +711 -0
  205. roadmap/adapters/sync/__init__.py +21 -0
  206. roadmap/adapters/sync/backend_factory.py +108 -0
  207. roadmap/adapters/sync/backends/__init__.py +15 -0
  208. roadmap/adapters/sync/backends/converters.py +209 -0
  209. roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
  210. roadmap/adapters/sync/backends/github_client.py +99 -0
  211. roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
  212. roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
  213. roadmap/adapters/sync/backends/services/__init__.py +1 -0
  214. roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
  215. roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
  216. roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
  217. roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
  218. roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
  219. roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
  220. roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
  221. roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
  222. roadmap/adapters/sync/services/__init__.py +11 -0
  223. roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
  224. roadmap/adapters/sync/services/conflict_converter.py +85 -0
  225. roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
  226. roadmap/adapters/sync/services/issue_state_service.py +243 -0
  227. roadmap/adapters/sync/services/local_change_filter.py +129 -0
  228. roadmap/adapters/sync/services/pull_result_processor.py +71 -0
  229. roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
  230. roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
  231. roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
  232. roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
  233. roadmap/adapters/sync/services/sync_linking_service.py +329 -0
  234. roadmap/adapters/sync/services/sync_report_service.py +62 -0
  235. roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
  236. roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
  237. roadmap/adapters/sync/sync_merge_engine.py +416 -0
  238. roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
  239. roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
  240. roadmap/application/services/__init__.py +1 -0
  241. roadmap/application/services/deduplicate_service.py +517 -0
  242. roadmap/application/use_cases/__init__.py +1 -0
  243. roadmap/common/__init__.py +308 -0
  244. roadmap/common/cache.py +209 -0
  245. roadmap/common/cli_errors.py +147 -0
  246. roadmap/common/configuration/__init__.py +33 -0
  247. roadmap/common/configuration/config_loader.py +264 -0
  248. roadmap/common/configuration/config_manager.py +164 -0
  249. roadmap/common/configuration/config_schema.py +145 -0
  250. roadmap/common/configuration/github/__init__.py +9 -0
  251. roadmap/common/configuration/github/config_manager.py +154 -0
  252. roadmap/common/configuration/github/token_resolver.py +84 -0
  253. roadmap/common/console.py +184 -0
  254. roadmap/common/constants.py +134 -0
  255. roadmap/common/datetime_parser.py +319 -0
  256. roadmap/common/error_formatter.py +84 -0
  257. roadmap/common/errors/__init__.py +143 -0
  258. roadmap/common/errors/error_base.py +73 -0
  259. roadmap/common/errors/error_file.py +82 -0
  260. roadmap/common/errors/error_git.py +67 -0
  261. roadmap/common/errors/error_handler.py +166 -0
  262. roadmap/common/errors/error_network.py +90 -0
  263. roadmap/common/errors/error_security.py +78 -0
  264. roadmap/common/errors/error_standards.py +561 -0
  265. roadmap/common/errors/error_validation.py +111 -0
  266. roadmap/common/errors/exceptions.py +432 -0
  267. roadmap/common/formatters/__init__.py +29 -0
  268. roadmap/common/formatters/base_table_formatter.py +96 -0
  269. roadmap/common/formatters/export/__init__.py +7 -0
  270. roadmap/common/formatters/export/issue_exporter.py +97 -0
  271. roadmap/common/formatters/helpers.py +28 -0
  272. roadmap/common/formatters/kanban/__init__.py +9 -0
  273. roadmap/common/formatters/kanban/layout.py +48 -0
  274. roadmap/common/formatters/kanban/organizer.py +81 -0
  275. roadmap/common/formatters/output/__init__.py +15 -0
  276. roadmap/common/formatters/tables/__init__.py +11 -0
  277. roadmap/common/formatters/tables/column_factory.py +312 -0
  278. roadmap/common/formatters/tables/issue_table.py +276 -0
  279. roadmap/common/formatters/tables/milestone_table.py +247 -0
  280. roadmap/common/formatters/tables/project_table.py +195 -0
  281. roadmap/common/formatters/text/__init__.py +57 -0
  282. roadmap/common/formatters/text/basic.py +217 -0
  283. roadmap/common/formatters/text/display.py +42 -0
  284. roadmap/common/formatters/text/duration.py +36 -0
  285. roadmap/common/formatters/text/operations.py +325 -0
  286. roadmap/common/formatters/text/status_badges.py +57 -0
  287. roadmap/common/initialization/github/__init__.py +11 -0
  288. roadmap/common/initialization/github/setup_service.py +238 -0
  289. roadmap/common/initialization/github/setup_validator.py +78 -0
  290. roadmap/common/logging/__init__.py +113 -0
  291. roadmap/common/logging/audit_logging.py +5 -0
  292. roadmap/common/logging/correlation.py +45 -0
  293. roadmap/common/logging/decorators.py +267 -0
  294. roadmap/common/logging/error_logging.py +217 -0
  295. roadmap/common/logging/formatters.py +124 -0
  296. roadmap/common/logging/loggers.py +68 -0
  297. roadmap/common/logging/performance_tracking.py +267 -0
  298. roadmap/common/logging/utils.py +335 -0
  299. roadmap/common/models/__init__.py +44 -0
  300. roadmap/common/models/cli_models.py +118 -0
  301. roadmap/common/models/config_models.py +199 -0
  302. roadmap/common/models/output_models.py +381 -0
  303. roadmap/common/observability/__init__.py +28 -0
  304. roadmap/common/observability/instrumentation.py +103 -0
  305. roadmap/common/observability/observability.py +76 -0
  306. roadmap/common/observability/otel_init.py +109 -0
  307. roadmap/common/output_formatter.py +393 -0
  308. roadmap/common/progress.py +379 -0
  309. roadmap/common/result.py +396 -0
  310. roadmap/common/security/__init__.py +48 -0
  311. roadmap/common/security/exceptions.py +13 -0
  312. roadmap/common/security/export_cleanup.py +79 -0
  313. roadmap/common/security/file_operations.py +104 -0
  314. roadmap/common/security/filename_sanitization.py +54 -0
  315. roadmap/common/security/logging.py +27 -0
  316. roadmap/common/security/path_validation.py +137 -0
  317. roadmap/common/security/temp_files.py +41 -0
  318. roadmap/common/services/__init__.py +65 -0
  319. roadmap/common/services/decorators.py +183 -0
  320. roadmap/common/services/logging_utils.py +208 -0
  321. roadmap/common/services/metrics.py +161 -0
  322. roadmap/common/services/performance.py +219 -0
  323. roadmap/common/services/profiling.py +288 -0
  324. roadmap/common/services/retry.py +288 -0
  325. roadmap/common/status_style_manager.py +114 -0
  326. roadmap/common/union_find.py +88 -0
  327. roadmap/common/update_constants.py +13 -0
  328. roadmap/common/utils/__init__.py +65 -0
  329. roadmap/common/utils/cli_helpers.py +381 -0
  330. roadmap/common/utils/file_utils.py +440 -0
  331. roadmap/common/utils/path_utils.py +38 -0
  332. roadmap/common/utils/status_utils.py +130 -0
  333. roadmap/common/utils/timezone_utils.py +480 -0
  334. roadmap/common/validation/__init__.py +37 -0
  335. roadmap/common/validation/field_validator.py +104 -0
  336. roadmap/common/validation/result.py +37 -0
  337. roadmap/common/validation/roadmap_validator.py +310 -0
  338. roadmap/common/validation/schema_validator.py +36 -0
  339. roadmap/common/validation/validators.py +50 -0
  340. roadmap/core/__init__.py +1 -0
  341. roadmap/core/domain/__init__.py +30 -0
  342. roadmap/core/domain/comment.py +24 -0
  343. roadmap/core/domain/health.py +15 -0
  344. roadmap/core/domain/issue.py +274 -0
  345. roadmap/core/domain/milestone.py +173 -0
  346. roadmap/core/domain/ports/__init__.py +1 -0
  347. roadmap/core/domain/ports/baseline_repository.py +24 -0
  348. roadmap/core/domain/ports/issue_repository.py +34 -0
  349. roadmap/core/domain/ports/remote_backend_port.py +31 -0
  350. roadmap/core/domain/project.py +130 -0
  351. roadmap/core/interfaces/__init__.py +109 -0
  352. roadmap/core/interfaces/assignee_validator.py +40 -0
  353. roadmap/core/interfaces/backend_factory.py +175 -0
  354. roadmap/core/interfaces/github.py +102 -0
  355. roadmap/core/interfaces/parsers.py +140 -0
  356. roadmap/core/interfaces/persistence.py +45 -0
  357. roadmap/core/interfaces/repositories.py +174 -0
  358. roadmap/core/interfaces/state_managers.py +111 -0
  359. roadmap/core/interfaces/state_storage.py +64 -0
  360. roadmap/core/interfaces/sync_backend.py +275 -0
  361. roadmap/core/interfaces/sync_services.py +119 -0
  362. roadmap/core/interfaces/sync_validators.py +180 -0
  363. roadmap/core/models/__init__.py +31 -0
  364. roadmap/core/models/sync_models.py +162 -0
  365. roadmap/core/models.py +115 -0
  366. roadmap/core/observability/__init__.py +17 -0
  367. roadmap/core/observability/sync_metrics.py +699 -0
  368. roadmap/core/repositories.py +200 -0
  369. roadmap/core/services/__init__.py +313 -0
  370. roadmap/core/services/baseline/__init__.py +1 -0
  371. roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
  372. roadmap/core/services/baseline/baseline_retriever.py +63 -0
  373. roadmap/core/services/baseline/baseline_selector.py +248 -0
  374. roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
  375. roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
  376. roadmap/core/services/comment/__init__.py +1 -0
  377. roadmap/core/services/comment/comment_service.py +277 -0
  378. roadmap/core/services/git/__init__.py +1 -0
  379. roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
  380. roadmap/core/services/github/__init__.py +1 -0
  381. roadmap/core/services/github/github_change_detector.py +162 -0
  382. roadmap/core/services/github/github_config_validator.py +148 -0
  383. roadmap/core/services/github/github_conflict_detector.py +179 -0
  384. roadmap/core/services/github/github_entity_classifier.py +51 -0
  385. roadmap/core/services/github/github_integration_service.py +439 -0
  386. roadmap/core/services/github/github_issue_client.py +346 -0
  387. roadmap/core/services/health/__init__.py +1 -0
  388. roadmap/core/services/health/backup_cleanup_service.py +202 -0
  389. roadmap/core/services/health/data_integrity_validator_service.py +90 -0
  390. roadmap/core/services/health/entity_health_scanner.py +460 -0
  391. roadmap/core/services/health/file_repair_service.py +193 -0
  392. roadmap/core/services/health/health_check_service.py +166 -0
  393. roadmap/core/services/health/health_models.py +80 -0
  394. roadmap/core/services/health/infrastructure_validator_service.py +328 -0
  395. roadmap/core/services/health/issue_health_scanner.py +263 -0
  396. roadmap/core/services/helpers/__init__.py +18 -0
  397. roadmap/core/services/helpers/status_change_helpers.py +88 -0
  398. roadmap/core/services/initialization/__init__.py +19 -0
  399. roadmap/core/services/initialization/utils.py +104 -0
  400. roadmap/core/services/initialization/validator.py +78 -0
  401. roadmap/core/services/initialization/workflow.py +182 -0
  402. roadmap/core/services/initialization_service.py +111 -0
  403. roadmap/core/services/issue/__init__.py +1 -0
  404. roadmap/core/services/issue/assignee_validation_service.py +312 -0
  405. roadmap/core/services/issue/issue_creation_service.py +309 -0
  406. roadmap/core/services/issue/issue_filter_service.py +302 -0
  407. roadmap/core/services/issue/issue_matching_service.py +127 -0
  408. roadmap/core/services/issue/issue_service.py +765 -0
  409. roadmap/core/services/issue/issue_update_service.py +144 -0
  410. roadmap/core/services/issue/start_issue_service.py +174 -0
  411. roadmap/core/services/issue_helpers/__init__.py +18 -0
  412. roadmap/core/services/issue_helpers/issue_filters.py +296 -0
  413. roadmap/core/services/milestone_service.py +438 -0
  414. roadmap/core/services/project/__init__.py +1 -0
  415. roadmap/core/services/project/project_service.py +434 -0
  416. roadmap/core/services/project/project_status_service.py +125 -0
  417. roadmap/core/services/project_init/__init__.py +22 -0
  418. roadmap/core/services/project_init/context_detection.py +146 -0
  419. roadmap/core/services/project_init/creation.py +76 -0
  420. roadmap/core/services/project_init/detection.py +51 -0
  421. roadmap/core/services/project_init/template.py +228 -0
  422. roadmap/core/services/status_change_service.py +88 -0
  423. roadmap/core/services/sync/__init__.py +1 -0
  424. roadmap/core/services/sync/batch_processor.py +290 -0
  425. roadmap/core/services/sync/conflict_resolver.py +154 -0
  426. roadmap/core/services/sync/dependency_resolver.py +367 -0
  427. roadmap/core/services/sync/duplicate_detector.py +771 -0
  428. roadmap/core/services/sync/duplicate_resolver.py +315 -0
  429. roadmap/core/services/sync/error_classification.py +470 -0
  430. roadmap/core/services/sync/performance.py +263 -0
  431. roadmap/core/services/sync/sync_change_computer.py +211 -0
  432. roadmap/core/services/sync/sync_checkpoint.py +348 -0
  433. roadmap/core/services/sync/sync_conflict_detector.py +147 -0
  434. roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
  435. roadmap/core/services/sync/sync_errors.py +383 -0
  436. roadmap/core/services/sync/sync_key_normalizer.py +222 -0
  437. roadmap/core/services/sync/sync_metadata_service.py +361 -0
  438. roadmap/core/services/sync/sync_plan.py +171 -0
  439. roadmap/core/services/sync/sync_plan_executor.py +447 -0
  440. roadmap/core/services/sync/sync_report.py +296 -0
  441. roadmap/core/services/sync/sync_state.py +173 -0
  442. roadmap/core/services/sync/sync_state_comparator.py +690 -0
  443. roadmap/core/services/sync/sync_state_manager.py +431 -0
  444. roadmap/core/services/sync/sync_state_normalizer.py +98 -0
  445. roadmap/core/services/sync/sync_three_way.py +78 -0
  446. roadmap/core/services/sync/three_way_merger.py +178 -0
  447. roadmap/core/services/utils/__init__.py +1 -0
  448. roadmap/core/services/utils/configuration_service.py +113 -0
  449. roadmap/core/services/utils/critical_path_calculator.py +418 -0
  450. roadmap/core/services/utils/dependency_analyzer.py +360 -0
  451. roadmap/core/services/utils/field_conflict_detector.py +126 -0
  452. roadmap/core/services/utils/remote_fetcher.py +202 -0
  453. roadmap/core/services/utils/remote_state_normalizer.py +110 -0
  454. roadmap/core/services/utils/retry_policy.py +51 -0
  455. roadmap/core/services/validator_base.py +130 -0
  456. roadmap/core/services/validators/__init__.py +28 -0
  457. roadmap/core/services/validators/_utils.py +20 -0
  458. roadmap/core/services/validators/archivable_issues_validator.py +71 -0
  459. roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
  460. roadmap/core/services/validators/backup_validator.py +89 -0
  461. roadmap/core/services/validators/data_integrity_validator.py +84 -0
  462. roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
  463. roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
  464. roadmap/core/services/validators/folder_structure_validator.py +165 -0
  465. roadmap/core/services/validators/health_status_utils.py +28 -0
  466. roadmap/core/services/validators/milestone_naming_validator.py +146 -0
  467. roadmap/core/services/validators/missing_headlines_validator.py +97 -0
  468. roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
  469. roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
  470. roadmap/core/utils/git_remote_parser.py +83 -0
  471. roadmap/core/utils/github_urls.py +66 -0
  472. roadmap/domain/validation.py +30 -0
  473. roadmap/infrastructure/__init__.py +55 -0
  474. roadmap/infrastructure/coordination/__init__.py +8 -0
  475. roadmap/infrastructure/coordination/coordinator_params.py +82 -0
  476. roadmap/infrastructure/coordination/core.py +446 -0
  477. roadmap/infrastructure/coordination/git_coordinator.py +160 -0
  478. roadmap/infrastructure/coordination/initialization.py +348 -0
  479. roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
  480. roadmap/infrastructure/coordination/issue_operations.py +456 -0
  481. roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
  482. roadmap/infrastructure/coordination/milestone_operations.py +201 -0
  483. roadmap/infrastructure/coordination/project_coordinator.py +113 -0
  484. roadmap/infrastructure/coordination/project_operations.py +144 -0
  485. roadmap/infrastructure/coordination/team_coordinator.py +107 -0
  486. roadmap/infrastructure/coordination/user_operations.py +150 -0
  487. roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
  488. roadmap/infrastructure/coordination_gateway.py +152 -0
  489. roadmap/infrastructure/git/__init__.py +6 -0
  490. roadmap/infrastructure/git/git_integration_ops.py +234 -0
  491. roadmap/infrastructure/git_gateway.py +35 -0
  492. roadmap/infrastructure/github_gateway.py +52 -0
  493. roadmap/infrastructure/maintenance/__init__.py +5 -0
  494. roadmap/infrastructure/maintenance/cleanup.py +483 -0
  495. roadmap/infrastructure/observability/__init__.py +7 -0
  496. roadmap/infrastructure/observability/health.py +196 -0
  497. roadmap/infrastructure/observability/health_formatter.py +73 -0
  498. roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
  499. roadmap/infrastructure/persistence_gateway.py +164 -0
  500. roadmap/infrastructure/security/__init__.py +1 -0
  501. roadmap/infrastructure/security/credentials.py +489 -0
  502. roadmap/infrastructure/sync_gateway.py +94 -0
  503. roadmap/infrastructure/validation/__init__.py +7 -0
  504. roadmap/infrastructure/validation/file_enumeration.py +219 -0
  505. roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
  506. roadmap/infrastructure/validation/github_validator.py +101 -0
  507. roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
  508. roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
  509. roadmap/infrastructure/validation_gateway.py +96 -0
  510. roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
  511. roadmap/settings.py +345 -0
  512. roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
  513. roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
  514. roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
  515. roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
  516. roadmap/version.py +381 -0
  517. roadmap_cli-0.1.1.dist-info/METADATA +448 -0
  518. roadmap_cli-0.1.1.dist-info/RECORD +521 -0
  519. roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
  520. roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
  521. roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
@@ -0,0 +1,174 @@
1
+ """Abstract repository interfaces for persistence.
2
+
3
+ These interfaces define contracts for data access and external integrations.
4
+ Implementations are in the infrastructure layer.
5
+
6
+ This follows the Dependency Inversion Principle:
7
+ - Core services depend on these abstractions
8
+ - Infrastructure implements these abstractions
9
+ - Services are decoupled from implementation details
10
+ """
11
+
12
+ import builtins
13
+ from abc import ABC, abstractmethod
14
+
15
+ from roadmap.core.domain import Issue, Milestone, Project
16
+
17
+
18
+ class IssueRepository(ABC):
19
+ """Abstract repository for issue persistence and retrieval.
20
+
21
+ Implementations handle:
22
+ - File-based storage
23
+ - Database operations
24
+ - Git integration
25
+ - Synchronization
26
+
27
+ Core services use this interface and don't care about implementation.
28
+ """
29
+
30
+ @abstractmethod
31
+ def get(self, issue_id: str) -> Issue | None:
32
+ """Retrieve a single issue by ID."""
33
+ pass
34
+
35
+ @abstractmethod
36
+ def list(self, _filters: dict | None = None) -> list[Issue]:
37
+ """List issues with optional filtering."""
38
+ pass
39
+
40
+ @abstractmethod
41
+ def create(self, issue: Issue) -> Issue:
42
+ """Create a new issue."""
43
+ pass
44
+
45
+ @abstractmethod
46
+ def update(self, issue: Issue) -> Issue:
47
+ """Update an existing issue."""
48
+ pass
49
+
50
+ @abstractmethod
51
+ def delete(self, issue_id: str) -> bool:
52
+ """Delete an issue by ID."""
53
+ pass
54
+
55
+ @abstractmethod
56
+ def bulk_update(self, issues: builtins.list[Issue]) -> builtins.list[Issue]:
57
+ """Update multiple issues at once."""
58
+ pass
59
+
60
+ @abstractmethod
61
+ def find_by_milestone(self, milestone_id: str) -> builtins.list[Issue]:
62
+ """Find all issues for a milestone."""
63
+ pass
64
+
65
+ @abstractmethod
66
+ def find_by_status(self, status: str) -> builtins.list[Issue]:
67
+ """Find issues by status."""
68
+ pass
69
+
70
+
71
+ class MilestoneRepository(ABC):
72
+ """Abstract repository for milestone persistence and retrieval.
73
+
74
+ Implementations handle:
75
+ - Milestone data storage
76
+ - Progress tracking
77
+ - Status management
78
+ - Synchronization
79
+
80
+ Core services use this interface and don't care about implementation.
81
+ """
82
+
83
+ @abstractmethod
84
+ def get(self, milestone_id: str) -> Milestone | None:
85
+ """Retrieve a single milestone by ID."""
86
+ pass
87
+
88
+ @abstractmethod
89
+ def list(self, project_id: str | None = None) -> list[Milestone]:
90
+ """List milestones, optionally filtered by project."""
91
+ pass
92
+
93
+ @abstractmethod
94
+ def create(self, milestone: Milestone) -> Milestone:
95
+ """Create a new milestone."""
96
+ pass
97
+
98
+ @abstractmethod
99
+ def update(self, milestone: Milestone) -> Milestone:
100
+ """Update an existing milestone."""
101
+ pass
102
+
103
+ @abstractmethod
104
+ def delete(self, milestone_id: str) -> bool:
105
+ """Delete a milestone by ID."""
106
+ pass
107
+
108
+ @abstractmethod
109
+ def get_progress(self, milestone_id: str) -> dict:
110
+ """Get progress metrics for a milestone."""
111
+ pass
112
+
113
+ @abstractmethod
114
+ def find_by_status(self, status: str) -> builtins.list[Milestone]:
115
+ """Find milestones by status."""
116
+ pass
117
+
118
+ @abstractmethod
119
+ def close(self, milestone_id: str) -> Milestone:
120
+ """Close/complete a milestone."""
121
+ pass
122
+
123
+
124
+ class ProjectRepository(ABC):
125
+ """Abstract repository for project persistence and retrieval.
126
+
127
+ Implementations handle:
128
+ - Project data storage
129
+ - Configuration management
130
+ - File system operations
131
+ - Synchronization
132
+
133
+ Core services use this interface and don't care about implementation.
134
+ """
135
+
136
+ @abstractmethod
137
+ def get(self, project_id: str) -> Project | None:
138
+ """Retrieve a single project by ID."""
139
+ pass
140
+
141
+ @abstractmethod
142
+ def list(self) -> list[Project]:
143
+ """List all projects."""
144
+ pass
145
+
146
+ @abstractmethod
147
+ def get_current(self) -> Project | None:
148
+ """Get the currently active project."""
149
+ pass
150
+
151
+ @abstractmethod
152
+ def create(self, project: Project) -> Project: # noqa: F841
153
+ """Create a new project."""
154
+ pass
155
+
156
+ @abstractmethod
157
+ def update(self, project: Project) -> Project: # noqa: F841
158
+ """Update an existing project."""
159
+ pass
160
+
161
+ @abstractmethod
162
+ def delete(self, project_id: str) -> bool:
163
+ """Delete a project by ID."""
164
+ pass
165
+
166
+ @abstractmethod
167
+ def get_status(self, project_id: str) -> dict:
168
+ """Get project status/health information."""
169
+ pass
170
+
171
+ @abstractmethod
172
+ def validate(self, project_id: str) -> bool:
173
+ """Validate project structure and integrity."""
174
+ pass
@@ -0,0 +1,111 @@
1
+ """Protocol definitions for focused state managers.
2
+
3
+ Splits the monolithic StateManager into focused, single-responsibility
4
+ interfaces for better testability and maintainability.
5
+ """
6
+
7
+ from typing import Any, Protocol
8
+
9
+
10
+ class ProjectStateManager(Protocol):
11
+ """Protocol for project state persistence."""
12
+
13
+ def create_project(self, project_data: dict[str, Any]) -> str:
14
+ """Create a new project."""
15
+ ...
16
+
17
+ def get_project(self, project_id: str) -> dict[str, Any] | None:
18
+ """Get project by ID."""
19
+ ...
20
+
21
+ def list_projects(self) -> list[dict[str, Any]]:
22
+ """List all projects."""
23
+ ...
24
+
25
+ def update_project(self, project_id: str, updates: dict[str, Any]) -> bool:
26
+ """Update project."""
27
+ ...
28
+
29
+ def delete_project(self, project_id: str) -> bool:
30
+ """Delete project."""
31
+ ...
32
+
33
+ def mark_project_archived(self, project_id: str, archived: bool = True) -> bool:
34
+ """Mark a project as archived or unarchived."""
35
+ ...
36
+
37
+
38
+ class MilestoneStateManager(Protocol):
39
+ """Protocol for milestone state persistence."""
40
+
41
+ def create_milestone(self, milestone_data: dict[str, Any]) -> str:
42
+ """Create a new milestone."""
43
+ ...
44
+
45
+ def get_milestone(self, milestone_id: str) -> dict[str, Any] | None:
46
+ """Get milestone by ID."""
47
+ ...
48
+
49
+ def update_milestone(self, milestone_id: str, updates: dict[str, Any]) -> bool:
50
+ """Update milestone."""
51
+ ...
52
+
53
+ def mark_milestone_archived(self, milestone_id: str, archived: bool = True) -> bool:
54
+ """Mark a milestone as archived or unarchived."""
55
+ ...
56
+
57
+
58
+ class IssueStateManager(Protocol):
59
+ """Protocol for issue state persistence."""
60
+
61
+ def create_issue(self, issue_data: dict[str, Any]) -> str:
62
+ """Create a new issue."""
63
+ ...
64
+
65
+ def get_issue(self, issue_id: str) -> dict[str, Any] | None:
66
+ """Get issue by ID."""
67
+ ...
68
+
69
+ def update_issue(self, issue_id: str, updates: dict[str, Any]) -> bool:
70
+ """Update issue."""
71
+ ...
72
+
73
+ def delete_issue(self, issue_id: str) -> bool:
74
+ """Delete issue."""
75
+ ...
76
+
77
+ def mark_issue_archived(self, issue_id: str, archived: bool = True) -> bool:
78
+ """Mark an issue as archived or unarchived."""
79
+ ...
80
+
81
+
82
+ class SyncStateManager(Protocol):
83
+ """Protocol for sync state persistence."""
84
+
85
+ def get_sync_state(self, key: str) -> str | None:
86
+ """Get sync state value."""
87
+ ...
88
+
89
+ def set_sync_state(self, key: str, value: str):
90
+ """Set sync state value."""
91
+ ...
92
+
93
+
94
+ class QueryStateManager(Protocol):
95
+ """Protocol for complex query operations on state."""
96
+
97
+ def get_all_issues(self) -> list[dict[str, Any]]:
98
+ """Get all issues from database."""
99
+ ...
100
+
101
+ def get_all_milestones(self) -> list[dict[str, Any]]:
102
+ """Get all milestones from database."""
103
+ ...
104
+
105
+ def get_milestone_progress(self, milestone_name: str) -> dict[str, int]:
106
+ """Get progress stats for a milestone."""
107
+ ...
108
+
109
+ def get_issues_by_status(self) -> dict[str, int]:
110
+ """Get issue counts by status."""
111
+ ...
@@ -0,0 +1,64 @@
1
+ """State manager interface for sync state operations.
2
+
3
+ Defines contract for managing sync state without importing storage implementations.
4
+ Breaks circular dependencies between core services and persistence layer.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+
9
+ from roadmap.core.services.sync.sync_state import SyncState
10
+
11
+
12
+ class SyncStateStorageInterface(ABC):
13
+ """Contract for sync state persistence operations."""
14
+
15
+ @abstractmethod
16
+ def load_sync_state(self) -> SyncState | None:
17
+ """Load the current sync state.
18
+
19
+ Returns:
20
+ SyncState object or None if not found
21
+ """
22
+ pass
23
+
24
+ @abstractmethod
25
+ def save_sync_state(self, state: SyncState) -> bool:
26
+ """Save sync state to storage.
27
+
28
+ Args:
29
+ state: SyncState object to save
30
+
31
+ Returns:
32
+ True if successful, False otherwise
33
+ """
34
+ pass
35
+
36
+ @abstractmethod
37
+ def clear_sync_state(self) -> bool:
38
+ """Clear the sync state.
39
+
40
+ Returns:
41
+ True if successful, False otherwise
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ def get_last_sync_time(self) -> str | None:
47
+ """Get the last sync timestamp.
48
+
49
+ Returns:
50
+ ISO format datetime string or None if never synced
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def update_last_sync_time(self, timestamp: str) -> bool:
56
+ """Update the last sync timestamp.
57
+
58
+ Args:
59
+ timestamp: ISO format datetime string
60
+
61
+ Returns:
62
+ True if successful, False otherwise
63
+ """
64
+ pass
@@ -0,0 +1,275 @@
1
+ """Protocol definitions for pluggable sync backends.
2
+
3
+ This module defines the SyncBackendInterface that all sync backends must implement,
4
+ enabling support for multiple sync targets (GitHub, vanilla Git, GitLab, etc.)
5
+ without coupling the core sync logic to specific implementations.
6
+
7
+ Updated to use Result<T, SyncError> pattern for explicit error handling.
8
+ """
9
+
10
+ from typing import Any, Protocol
11
+
12
+ from roadmap.common.result import Result
13
+ from roadmap.core.domain.issue import Issue
14
+ from roadmap.core.models.sync_models import SyncIssue, SyncMilestone, SyncProject
15
+ from roadmap.core.services.sync.sync_errors import SyncError
16
+
17
+
18
+ class SyncConflict:
19
+ """Represents a conflict during sync operations."""
20
+
21
+ def __init__(
22
+ self,
23
+ issue_id: str,
24
+ local_version: Issue | None,
25
+ remote_version: dict[str, Any] | None,
26
+ conflict_type: str,
27
+ ):
28
+ """Initialize a sync conflict.
29
+
30
+ Args:
31
+ issue_id: ID of the conflicting issue
32
+ local_version: Local Issue object (may be None if deleted remotely)
33
+ remote_version: Remote issue dict (may be None if deleted locally)
34
+ conflict_type: Type of conflict ('both_modified', 'deleted_locally', 'deleted_remotely')
35
+ """
36
+ self.issue_id = issue_id
37
+ self.local_version = local_version
38
+ self.remote_version = remote_version
39
+ self.conflict_type = conflict_type
40
+
41
+
42
+ class SyncReport:
43
+ """Report of sync operations with status and conflicts."""
44
+
45
+ def __init__(self):
46
+ """Initialize an empty sync report."""
47
+ self.pushed: list[str] = [] # Issue IDs successfully pushed
48
+ self.pulled: list[str] = [] # Issue IDs successfully pulled
49
+ self.conflicts: list[SyncConflict] = [] # Issues with conflicts
50
+ self.errors: dict[str, str] = {} # Issue ID -> error message
51
+ self.error: str | None = None # Overall sync error (if fatal)
52
+
53
+
54
+ class SyncBackendInterface(Protocol):
55
+ """Abstract interface for sync backends.
56
+
57
+ Defines the contract that all sync backends must implement to enable
58
+ pluggable sync functionality. Allows support for GitHub, vanilla Git,
59
+ GitLab, Jira, and other sync targets.
60
+
61
+ All methods should handle errors gracefully and return status/report objects
62
+ rather than raising exceptions, enabling dry-run and conflict resolution
63
+ workflows.
64
+ """
65
+
66
+ def authenticate(self) -> Result[bool, SyncError]:
67
+ """Verify credentials and remote connectivity.
68
+
69
+ Returns:
70
+ Ok(True) if authentication succeeds
71
+ Err(SyncError) with authentication error details
72
+
73
+ Notes:
74
+ - No longer raises exceptions
75
+ - Returns Err with AUTHENTICATION_FAILED error type on failure
76
+ - May return TOKEN_EXPIRED or PERMISSION_DENIED error types
77
+ """
78
+ ...
79
+
80
+ def get_backend_name(self) -> str:
81
+ """Get the canonical name of this backend.
82
+
83
+ Returns:
84
+ Backend name (e.g., 'github', 'git', 'gitlab')
85
+ Used as key in Issue.remote_ids dict to track remote issue IDs.
86
+ """
87
+ ...
88
+
89
+ def get_issues(self) -> Result[dict[str, SyncIssue], SyncError]:
90
+ """Fetch all issues from remote.
91
+
92
+ Returns:
93
+ Ok(dict) mapping issue_id -> SyncIssue on success
94
+ Err(SyncError) with error details on failure
95
+
96
+ Notes:
97
+ - Backends normalize their API responses to SyncIssue format
98
+ - All fields are populated from backend data, including raw_response
99
+ - May return NETWORK_ERROR, API_RATE_LIMIT, or other error types
100
+ """
101
+ ...
102
+
103
+ def push_issues(self, local_issues: list[Issue]) -> Result[SyncReport, SyncError]:
104
+ """Push multiple local issues to remote.
105
+
106
+ Args:
107
+ local_issues: List of Issue objects to push
108
+
109
+ Returns:
110
+ Ok(SyncReport) with pushed, conflicts, and errors on success
111
+ Err(SyncError) with fatal error details if operation cannot proceed
112
+
113
+ Notes:
114
+ - Should not stop on first error; attempt all issues
115
+ - Individual issue errors go in report.errors dict
116
+ - Fatal errors (auth, network) return Err
117
+ - Populate report.pushed with successful issue IDs
118
+ - Populate report.conflicts with conflicting issues
119
+ """
120
+ ...
121
+
122
+ def push_issue(self, local_issue: Issue) -> Result[bool, SyncError]:
123
+ """Push a single local issue to remote.
124
+
125
+ Args:
126
+ local_issue: The Issue object to push to remote
127
+
128
+ Returns:
129
+ Ok(True) if push succeeds
130
+ Err(SyncError) with error details if push fails
131
+
132
+ Notes:
133
+ - Default implementation: delegates to push_issues()
134
+ - Backends may override for optimization
135
+ - Idempotent: pushing same issue twice should be safe
136
+ """
137
+ report_result = self.push_issues([local_issue])
138
+ if report_result.is_err():
139
+ return report_result # type: ignore[return-value]
140
+
141
+ report = report_result.unwrap()
142
+ if len(report.pushed) > 0 and len(report.errors) == 0:
143
+ from roadmap.common.result import Ok
144
+
145
+ return Ok(True)
146
+ else:
147
+ from roadmap.common.result import Err
148
+ from roadmap.core.services.sync.sync_errors import SyncError, SyncErrorType
149
+
150
+ error_msg = report.errors.get(str(local_issue.id), "Push failed")
151
+ return Err(
152
+ SyncError(
153
+ error_type=SyncErrorType.UNKNOWN_ERROR,
154
+ message=error_msg,
155
+ entity_type="Issue",
156
+ entity_id=str(local_issue.id),
157
+ )
158
+ )
159
+
160
+ def pull_issues(self, issue_ids: list[str]) -> Result[SyncReport, SyncError]:
161
+ """Pull specified remote issues to local.
162
+
163
+ Args:
164
+ issue_ids: List of remote issue IDs to pull. Empty list means pull nothing.
165
+
166
+ Returns:
167
+ Ok(SyncReport) with pulled, conflicts, and errors on success
168
+ Err(SyncError) with fatal error details if operation cannot proceed
169
+
170
+ Notes:
171
+ - Should detect conflicts with existing local issues
172
+ - Individual issue errors go in report.errors dict
173
+ - Fatal errors (auth, network) return Err
174
+ - Populate report.pulled with issue IDs successfully integrated
175
+ - Populate report.conflicts with conflicting issues
176
+ - Backend can optimize this (batch API calls, parallel processing, etc.)
177
+ """
178
+ ...
179
+
180
+ def pull_issue(self, issue_id: str) -> Result[bool, SyncError]:
181
+ """Pull a single remote issue to local.
182
+
183
+ Args:
184
+ issue_id: The remote issue ID to pull
185
+
186
+ Returns:
187
+ Ok(True) if pull succeeds
188
+ Err(SyncError) with error details if pull fails
189
+
190
+ Notes:
191
+ - Default implementation: delegates to pull_issues()
192
+ - Backends may override for optimization
193
+ - Fetches the remote issue and updates local
194
+ """
195
+ report_result = self.pull_issues([issue_id])
196
+ if report_result.is_err():
197
+ return report_result # type: ignore[return-value]
198
+
199
+ report = report_result.unwrap()
200
+ if len(report.pulled) > 0 and len(report.errors) == 0:
201
+ from roadmap.common.result import Ok
202
+
203
+ return Ok(True)
204
+ else:
205
+ from roadmap.common.result import Err
206
+ from roadmap.core.services.sync.sync_errors import SyncError, SyncErrorType
207
+
208
+ error_msg = report.errors.get(issue_id, "Pull failed")
209
+ return Err(
210
+ SyncError(
211
+ error_type=SyncErrorType.UNKNOWN_ERROR,
212
+ message=error_msg,
213
+ entity_type="Issue",
214
+ entity_id=issue_id,
215
+ )
216
+ )
217
+
218
+ def get_conflict_resolution_options(self, conflict: SyncConflict) -> list[str]:
219
+ """Get available resolution strategies for a conflict.
220
+
221
+ Args:
222
+ conflict: The SyncConflict to resolve
223
+
224
+ Returns:
225
+ List of resolution option codes (e.g., ['use_local', 'use_remote', 'merge'])
226
+
227
+ Notes:
228
+ - Different backends may support different strategies
229
+ - At minimum should support 'use_local' and 'use_remote'
230
+ - Merge strategies optional and backend-specific
231
+ """
232
+ ...
233
+
234
+ def resolve_conflict(self, conflict: SyncConflict, resolution: str) -> bool:
235
+ """Resolve a sync conflict using specified strategy.
236
+
237
+ Args:
238
+ conflict: The SyncConflict to resolve
239
+ resolution: The resolution strategy code (from get_conflict_resolution_options)
240
+
241
+ Returns:
242
+ True if resolution succeeds, False otherwise.
243
+
244
+ Notes:
245
+ - Should not raise exceptions; return False on failure
246
+ - After resolution, issue should be in consistent state
247
+ - May involve local or remote modifications
248
+ """
249
+ ...
250
+
251
+ def get_milestones(self) -> Result[dict[str, SyncMilestone], SyncError]:
252
+ """Fetch all milestones from remote.
253
+
254
+ Returns:
255
+ Ok(dict) mapping milestone_id -> SyncMilestone on success
256
+ Err(SyncError) with error details on failure
257
+
258
+ Notes:
259
+ - Empty dict in Ok indicates no milestones exist
260
+ - Network errors return Err
261
+ """
262
+ ...
263
+
264
+ def get_projects(self) -> Result[dict[str, SyncProject], SyncError]:
265
+ """Fetch all projects from remote.
266
+
267
+ Returns:
268
+ Ok(dict) mapping project_id -> SyncProject on success
269
+ Err(SyncError) with error details on failure
270
+
271
+ Notes:
272
+ - Empty dict in Ok indicates no projects exist
273
+ - Network errors return Err
274
+ """
275
+ ...