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,561 @@
1
+ """Standardized error handling patterns and decorators.
2
+
3
+ This module provides reusable decorators and context managers for consistent
4
+ error handling across the codebase. All decorators automatically log errors
5
+ with rich context, support retry logic for transient failures, and convert
6
+ exceptions to appropriate RoadmapException types.
7
+
8
+ Key patterns:
9
+ @safe_operation - Wraps CRUD operations with automatic error handling
10
+ @log_operation - Lightweight operation logging without exception conversion
11
+ with_error_handling() - Context manager for error handling blocks
12
+ ErrorContext - Builder for rich error context at error time
13
+
14
+ Example:
15
+ @safe_operation(OperationType.CREATE, "Issue")
16
+ def create_issue(title: str) -> Issue:
17
+ return Issue(title) # Errors automatically caught, logged, wrapped
18
+
19
+ @safe_operation(OperationType.SYNC, "GitHub", retryable=True, max_retries=3)
20
+ def fetch_issues(project_id: str) -> list[dict]:
21
+ return github_api.get_issues(project_id) # Retries on transient failures
22
+ """
23
+
24
+ import time
25
+ from collections.abc import Callable
26
+ from contextlib import contextmanager
27
+ from functools import wraps
28
+ from typing import Any, ParamSpec, TypeVar
29
+
30
+ from roadmap.common.logging import get_logger
31
+ from roadmap.common.logging.error_logging import (
32
+ is_error_recoverable,
33
+ log_error_with_context,
34
+ )
35
+
36
+ logger = get_logger(__name__)
37
+
38
+ P = ParamSpec("P")
39
+ T = TypeVar("T")
40
+
41
+
42
+ # ============================================================================
43
+ # STANDARD OPERATION TYPES
44
+ # ============================================================================
45
+
46
+
47
+ class OperationType:
48
+ """Standard operation type constants for consistent error handling."""
49
+
50
+ CREATE = "create"
51
+ READ = "read"
52
+ UPDATE = "update"
53
+ DELETE = "delete"
54
+ SYNC = "sync"
55
+ IMPORT = "import"
56
+ EXPORT = "export"
57
+ VALIDATE = "validate"
58
+ AUTHENTICATE = "authenticate"
59
+ FETCH = "fetch"
60
+ SAVE = "save"
61
+
62
+
63
+ # ============================================================================
64
+ # ERROR CONTEXT BUILDER - Captures rich context at error time
65
+ # ============================================================================
66
+
67
+
68
+ class ErrorContext:
69
+ """Builder for rich error context to include in logs and exceptions.
70
+
71
+ Fluent API for capturing operation context that will be included in
72
+ error logs and exception messages. Supports context like entity IDs,
73
+ input parameters, operation state, and suggested recovery actions.
74
+
75
+ Example:
76
+ context = (
77
+ ErrorContext("sync", "Issues")
78
+ .with_entity_id(project_id)
79
+ .with_input(force=True, filters={})
80
+ .with_state(synced_count=10, failed_count=1)
81
+ .with_recovery("retry", "Check network connection")
82
+ )
83
+ context_dict = context.build()
84
+ """
85
+
86
+ def __init__(self, operation: str, entity_type: str | None = None):
87
+ """Initialize error context.
88
+
89
+ Args:
90
+ operation: Name of the operation (e.g., "create", "sync")
91
+ entity_type: Type of entity being operated on (e.g., "Issue", "Milestone")
92
+ """
93
+ self.data: dict[str, Any] = {
94
+ "operation": operation,
95
+ }
96
+ if entity_type:
97
+ self.data["entity_type"] = entity_type
98
+
99
+ def with_entity_id(self, entity_id: str | int) -> "ErrorContext":
100
+ """Add entity ID to context."""
101
+ self.data["entity_id"] = str(entity_id)
102
+ return self
103
+
104
+ def with_input(self, **kwargs: Any) -> "ErrorContext":
105
+ """Add input parameters to context."""
106
+ if kwargs:
107
+ self.data["input"] = kwargs
108
+ return self
109
+
110
+ def with_state(self, **kwargs: Any) -> "ErrorContext":
111
+ """Add operation state to context."""
112
+ if kwargs:
113
+ self.data["state"] = kwargs
114
+ return self
115
+
116
+ def with_recovery(self, action: str, details: str = "") -> "ErrorContext":
117
+ """Add recovery suggestion to context."""
118
+ self.data["recovery_action"] = action
119
+ if details:
120
+ self.data["recovery_details"] = details
121
+ return self
122
+
123
+ def with_attempt(self, attempt: int, max_retries: int) -> "ErrorContext":
124
+ """Add retry attempt information to context."""
125
+ self.data["retry_attempt"] = attempt
126
+ self.data["max_retries"] = max_retries
127
+ return self
128
+
129
+ def build(self) -> dict[str, Any]:
130
+ """Build and return the context dictionary."""
131
+ return self.data.copy()
132
+
133
+
134
+ # ============================================================================
135
+ # DECORATOR: @safe_operation - Wraps CRUD operations with standard handling
136
+ # ============================================================================
137
+
138
+
139
+ def safe_operation(
140
+ operation_type: str,
141
+ entity_type: str | None = None,
142
+ include_traceback: bool = False,
143
+ retryable: bool = False,
144
+ max_retries: int = 1,
145
+ retry_delay: float = 0.5,
146
+ retry_backoff: float = 2.0,
147
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]:
148
+ """Execute operation safely with standard error handling.
149
+
150
+ Wraps create, read, update, delete, sync operations with:
151
+ - Automatic error classification and logging with rich context
152
+ - Graceful exception conversion to appropriate RoadmapException
153
+ - Optional retry on recoverable errors with exponential backoff
154
+ - Traceback inclusion for debugging when needed
155
+
156
+ Args:
157
+ operation_type: Type of operation (OperationType constants)
158
+ entity_type: Type of entity being operated on (Issue, Milestone, etc.)
159
+ include_traceback: Whether to include full traceback in error logs
160
+ retryable: Whether to retry on recoverable errors
161
+ max_retries: Maximum retry attempts (if retryable=True)
162
+ retry_delay: Initial delay in seconds between retries
163
+ retry_backoff: Multiplier for delay after each retry (exponential backoff)
164
+
165
+ Returns:
166
+ Decorator function
167
+
168
+ Example:
169
+ @safe_operation(OperationType.CREATE, "Issue")
170
+ def create_issue(title: str, description: str) -> Issue:
171
+ # Function body - errors are automatically caught and logged
172
+ return Issue(title, description)
173
+
174
+ @safe_operation(OperationType.DELETE, "Issue", retryable=True, max_retries=3)
175
+ def delete_issue(issue_id: str) -> None:
176
+ # Will retry up to 3 times on network/timeout errors
177
+ pass
178
+
179
+ Raises:
180
+ CreateError: If operation_type is CREATE and original error occurs
181
+ UpdateError: If operation_type is UPDATE and original error occurs
182
+ DeleteError: If operation_type is DELETE and original error occurs
183
+ RoadmapException: For other operation types
184
+ """
185
+ from roadmap.common.errors.exceptions import (
186
+ CreateError,
187
+ DeleteError,
188
+ RoadmapException,
189
+ UpdateError,
190
+ )
191
+
192
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
193
+ @wraps(func)
194
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: # type: ignore[return-value]
195
+ context = ErrorContext(operation_type, entity_type)
196
+
197
+ # Capture first string/int argument as entity_id if available
198
+ if args and isinstance(args[0], str | int):
199
+ context.with_entity_id(args[0])
200
+ elif "id" in kwargs and isinstance(kwargs["id"], str | int):
201
+ context.with_entity_id(kwargs["id"])
202
+
203
+ # Capture input parameters for debugging
204
+ context.with_input(**kwargs)
205
+
206
+ attempt = 0
207
+ last_error: Exception | None = None
208
+ delay = retry_delay
209
+
210
+ while attempt < max_retries:
211
+ try:
212
+ if attempt > 0:
213
+ logger.debug(
214
+ "retry_attempt",
215
+ attempt=attempt,
216
+ max_retries=max_retries,
217
+ entity_type=entity_type,
218
+ operation_type=operation_type,
219
+ )
220
+ return func(*args, **kwargs)
221
+
222
+ except RoadmapException:
223
+ # Already a roadmap exception - let it propagate
224
+ raise
225
+
226
+ except Exception as e:
227
+ # Lazy import to avoid infrastructure dependency in common layer
228
+ from roadmap.common.logging.error_logging import (
229
+ is_error_recoverable,
230
+ log_error_with_context,
231
+ )
232
+
233
+ last_error = e
234
+ is_recoverable = is_error_recoverable(e)
235
+
236
+ # Build context with retry information if applicable
237
+ error_context = context.build()
238
+ if retryable and max_retries > 1:
239
+ error_context["retry_attempt"] = attempt + 1
240
+ error_context["max_retries"] = max_retries
241
+
242
+ # Log the error with full context
243
+ log_error_with_context(
244
+ e,
245
+ f"{operation_type}_{entity_type or 'unknown'}",
246
+ entity_type=entity_type,
247
+ additional_context=error_context,
248
+ include_traceback=include_traceback,
249
+ )
250
+
251
+ # Check if we should retry
252
+ if retryable and is_recoverable and attempt < max_retries - 1:
253
+ attempt += 1
254
+ logger.info(
255
+ "retry_in_progress",
256
+ attempt=attempt,
257
+ max_retries=max_retries,
258
+ error_type=type(e).__name__,
259
+ delay_seconds=delay,
260
+ operation_type=operation_type,
261
+ )
262
+ time.sleep(delay)
263
+ delay *= retry_backoff
264
+ continue # Retry
265
+
266
+ # Convert to appropriate RoadmapException if not retrying
267
+ attempt_info = (
268
+ f" (after {attempt} retries)"
269
+ if retryable and attempt > 0
270
+ else ""
271
+ )
272
+ error_msg = f"{str(e)}{attempt_info}"
273
+
274
+ if operation_type == OperationType.CREATE:
275
+ raise CreateError(
276
+ entity_type or "entity",
277
+ error_msg,
278
+ ) from e
279
+ elif operation_type == OperationType.UPDATE:
280
+ raise UpdateError(
281
+ entity_type or "entity",
282
+ error_msg,
283
+ ) from e
284
+ elif operation_type == OperationType.DELETE:
285
+ raise DeleteError(
286
+ entity_type or "entity",
287
+ error_msg,
288
+ ) from e
289
+ else:
290
+ raise RoadmapException(
291
+ domain_message=f"{operation_type} failed: {error_msg}",
292
+ user_message=f"Failed to {operation_type}: {str(e)}",
293
+ ) from e
294
+
295
+ # If we exhausted retries, raise the last error
296
+ if last_error:
297
+ raise last_error
298
+
299
+ return wrapper
300
+
301
+ return decorator
302
+
303
+
304
+ # ============================================================================
305
+ # DECORATOR: @log_operation - For operations that don't need exception handling
306
+ # ============================================================================
307
+
308
+
309
+ def log_operation(
310
+ operation_type: str,
311
+ entity_type: str | None = None,
312
+ log_inputs: bool = True,
313
+ log_output: bool = False,
314
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]:
315
+ """Lightweight decorator to log operation execution without error handling.
316
+
317
+ Use for operations that are already well-protected or don't need automatic
318
+ error conversion. Logs the operation, inputs, and optionally outputs.
319
+
320
+ Args:
321
+ operation_type: Type of operation
322
+ entity_type: Type of entity
323
+ log_inputs: Whether to log input arguments
324
+ log_output: Whether to log the return value
325
+
326
+ Returns:
327
+ Decorator function
328
+
329
+ Example:
330
+ @log_operation(OperationType.READ, "Issue", log_inputs=True)
331
+ def get_issue(issue_id: str) -> Issue:
332
+ # Log entry/exit but let exceptions propagate normally
333
+ return issues_db.get(issue_id)
334
+ """
335
+
336
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
337
+ @wraps(func)
338
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
339
+ if log_inputs:
340
+ logger.info(
341
+ "operation_started",
342
+ operation_type=operation_type,
343
+ entity_type=entity_type,
344
+ **kwargs,
345
+ )
346
+
347
+ result = func(*args, **kwargs)
348
+
349
+ if log_output and result:
350
+ logger.info(
351
+ "operation_completed",
352
+ operation_type=operation_type,
353
+ entity_type=entity_type,
354
+ )
355
+
356
+ return result
357
+
358
+ return wrapper
359
+
360
+ return decorator
361
+
362
+
363
+ # ============================================================================
364
+ # CONTEXT MANAGER: with_error_handling - For non-function error handling
365
+ # ============================================================================
366
+
367
+
368
+ @contextmanager
369
+ def with_error_handling(
370
+ operation: str,
371
+ entity_type: str | None = None,
372
+ entity_id: str | int | None = None,
373
+ fail_silently: bool = False,
374
+ include_traceback: bool = False,
375
+ ):
376
+ """Context manager for error handling in operation blocks.
377
+
378
+ Use when you have a block of code that might fail and you want consistent
379
+ error logging and exception wrapping without using a decorator.
380
+
381
+ Args:
382
+ operation: Name of the operation (e.g., "sync", "export")
383
+ entity_type: Type of entity involved
384
+ entity_id: ID of the entity being operated on
385
+ fail_silently: If True, log error but don't re-raise
386
+ include_traceback: Include full traceback in logs
387
+
388
+ Yields:
389
+ None
390
+
391
+ Raises:
392
+ RoadmapException: If error occurs and fail_silently=False
393
+
394
+ Example:
395
+ with with_error_handling("sync", "Issues", entity_id=project_id):
396
+ # Code that might fail
397
+ sync_issues(project_id)
398
+
399
+ # Error is automatically logged and wrapped in appropriate exception
400
+ """
401
+ from roadmap.common.errors.exceptions import RoadmapException
402
+
403
+ try:
404
+ yield
405
+ except RoadmapException:
406
+ raise
407
+ except Exception as e:
408
+ context = {
409
+ "operation": operation,
410
+ "entity_type": entity_type,
411
+ "entity_id": str(entity_id) if entity_id else None,
412
+ }
413
+
414
+ log_error_with_context(
415
+ e,
416
+ operation,
417
+ entity_type=entity_type,
418
+ entity_id=str(entity_id) if entity_id else None,
419
+ additional_context=context,
420
+ include_traceback=include_traceback,
421
+ )
422
+
423
+ if not fail_silently:
424
+ raise RoadmapException(
425
+ domain_message=f"{operation} failed: {str(e)}",
426
+ user_message=f"Failed to {operation}",
427
+ ) from e
428
+
429
+
430
+ # ============================================================================
431
+ # RECOVERY ACTION HANDLERS - For application-level recovery
432
+ # ============================================================================
433
+
434
+
435
+ class RecoveryAction:
436
+ """Handles recovery from common error scenarios.
437
+
438
+ Provides static methods for common recovery patterns like retry with
439
+ backoff, handling missing files, and suggesting recovery actions.
440
+
441
+ Example:
442
+ try:
443
+ result = risky_operation()
444
+ except ConnectionError as e:
445
+ if RecoveryAction.is_retryable(e):
446
+ result = RecoveryAction.retry_with_backoff(risky_operation)
447
+ """
448
+
449
+ @staticmethod
450
+ def retry_with_backoff(
451
+ func: Callable[..., T],
452
+ max_attempts: int = 3,
453
+ initial_delay: float = 0.5,
454
+ backoff_factor: float = 2.0,
455
+ ) -> T: # type: ignore[return-value]
456
+ """Retry a function with exponential backoff.
457
+
458
+ Args:
459
+ func: Function to retry
460
+ max_attempts: Maximum number of attempts
461
+ initial_delay: Initial delay in seconds between retries
462
+ backoff_factor: Multiplier for delay after each retry
463
+
464
+ Returns:
465
+ Return value from successful function call
466
+
467
+ Raises:
468
+ Last exception if all retries exhausted
469
+ """
470
+ last_error: Exception | None = None
471
+ delay = initial_delay
472
+
473
+ for attempt in range(max_attempts):
474
+ try:
475
+ return func()
476
+ except Exception as e:
477
+ last_error = e
478
+ if attempt < max_attempts - 1 and is_error_recoverable(e):
479
+ time.sleep(delay)
480
+ delay *= backoff_factor
481
+ else:
482
+ raise
483
+
484
+ if last_error:
485
+ raise last_error
486
+
487
+ @staticmethod
488
+ def is_retryable(error: Exception) -> bool:
489
+ """Check if an error is retryable.
490
+
491
+ Args:
492
+ error: Exception to check
493
+
494
+ Returns:
495
+ True if error is recoverable/retryable
496
+ """
497
+ return is_error_recoverable(error)
498
+
499
+ @staticmethod
500
+ def handle_missing_file(
501
+ filepath: str, create_default: bool = True, content: str = ""
502
+ ) -> bool:
503
+ """Handle missing file error by creating default.
504
+
505
+ Args:
506
+ filepath: Path to the missing file
507
+ create_default: Whether to create a default file
508
+ content: Content to write to the file
509
+
510
+ Returns:
511
+ True if file was created successfully, False otherwise
512
+ """
513
+ if not create_default:
514
+ return False
515
+
516
+ from pathlib import Path
517
+
518
+ try:
519
+ Path(filepath).parent.mkdir(parents=True, exist_ok=True)
520
+ Path(filepath).write_text(content)
521
+ logger.info("created_missing_file", filepath=filepath)
522
+ return True
523
+ except Exception as e:
524
+ logger.error("failed_to_create_file", filepath=filepath, error=str(e))
525
+ return False
526
+
527
+ @staticmethod
528
+ def handle_permission_error(filepath: str) -> str:
529
+ """Suggest recovery action for permission error.
530
+
531
+ Args:
532
+ filepath: Path that had permission error
533
+
534
+ Returns:
535
+ Suggested recovery command
536
+ """
537
+ return f"Check file permissions: chmod u+rw {filepath}"
538
+
539
+ @staticmethod
540
+ def handle_connection_error(service: str) -> str:
541
+ """Suggest recovery action for connection error.
542
+
543
+ Args:
544
+ service: Name of service that couldn't be reached
545
+
546
+ Returns:
547
+ Suggested recovery action
548
+ """
549
+ return (
550
+ f"Unable to reach {service}. Check network connection and service status."
551
+ )
552
+
553
+
554
+ __all__ = [
555
+ "OperationType",
556
+ "ErrorContext",
557
+ "safe_operation",
558
+ "log_operation",
559
+ "with_error_handling",
560
+ "RecoveryAction",
561
+ ]
@@ -0,0 +1,111 @@
1
+ """Validation related error classes."""
2
+
3
+ from typing import Any
4
+
5
+ from roadmap.common.errors.error_base import ErrorCategory, ErrorSeverity, RoadmapError
6
+
7
+
8
+ class ValidationError(RoadmapError):
9
+ """Errors related to data validation."""
10
+
11
+ def __init__(
12
+ self, message: str, field: str | None = None, value: Any | None = None, **kwargs
13
+ ):
14
+ """Initialize ValidationError.
15
+
16
+ Args:
17
+ message: Error message.
18
+ field: Name of the field that failed validation.
19
+ value: Value that failed validation.
20
+ **kwargs: Additional arguments passed to parent class.
21
+ """
22
+ context = kwargs.get("context", {})
23
+ if field:
24
+ context["field"] = field
25
+ if value is not None:
26
+ context["value"] = str(value)
27
+
28
+ super().__init__(
29
+ message,
30
+ severity=kwargs.get("severity", ErrorSeverity.MEDIUM),
31
+ category=ErrorCategory.VALIDATION,
32
+ context=context,
33
+ cause=kwargs.get("cause"),
34
+ )
35
+ self.field = field
36
+ self.value = value
37
+
38
+
39
+ class StateError(RoadmapError):
40
+ """Raised when operation is invalid for current state."""
41
+
42
+ def __init__(self, message: str, current_state: str | None = None, **kwargs):
43
+ """Initialize StateError.
44
+
45
+ Args:
46
+ message: Error message.
47
+ current_state: Current state that made the operation invalid.
48
+ **kwargs: Additional arguments passed to parent class.
49
+ """
50
+ context = kwargs.get("context", {})
51
+ if current_state:
52
+ context["current_state"] = current_state
53
+
54
+ super().__init__(
55
+ message,
56
+ severity=kwargs.get("severity", ErrorSeverity.MEDIUM),
57
+ category=ErrorCategory.VALIDATION,
58
+ context=context,
59
+ cause=kwargs.get("cause"),
60
+ )
61
+ self.current_state = current_state
62
+
63
+
64
+ class IssueNotFoundError(RoadmapError):
65
+ """Raised when an issue cannot be found."""
66
+
67
+ def __init__(self, message: str, issue_id: str | None = None, **kwargs):
68
+ """Initialize IssueNotFoundError.
69
+
70
+ Args:
71
+ message: Error message.
72
+ issue_id: ID of the issue that was not found.
73
+ **kwargs: Additional arguments passed to parent class.
74
+ """
75
+ context = kwargs.get("context", {})
76
+ if issue_id:
77
+ context["issue_id"] = issue_id
78
+
79
+ super().__init__(
80
+ message,
81
+ severity=kwargs.get("severity", ErrorSeverity.MEDIUM),
82
+ category=ErrorCategory.VALIDATION,
83
+ context=context,
84
+ cause=kwargs.get("cause"),
85
+ )
86
+ self.issue_id = issue_id
87
+
88
+
89
+ class MilestoneNotFoundError(RoadmapError):
90
+ """Raised when a milestone cannot be found."""
91
+
92
+ def __init__(self, message: str, milestone_name: str | None = None, **kwargs):
93
+ """Initialize MilestoneNotFoundError.
94
+
95
+ Args:
96
+ message: Error message.
97
+ milestone_name: Name of the milestone that was not found.
98
+ **kwargs: Additional arguments passed to parent class.
99
+ """
100
+ context = kwargs.get("context", {})
101
+ if milestone_name:
102
+ context["milestone"] = milestone_name
103
+
104
+ super().__init__(
105
+ message,
106
+ severity=kwargs.get("severity", ErrorSeverity.MEDIUM),
107
+ category=ErrorCategory.VALIDATION,
108
+ context=context,
109
+ cause=kwargs.get("cause"),
110
+ )
111
+ self.milestone_name = milestone_name