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,470 @@
1
+ """Error classification system for sync operations.
2
+
3
+ This module provides categorization and aggregation of sync errors
4
+ to present clear, actionable error summaries to users.
5
+ """
6
+
7
+ from collections import defaultdict
8
+ from dataclasses import dataclass
9
+ from enum import StrEnum
10
+ from typing import Any
11
+
12
+ from roadmap.common.logging import get_logger
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class ErrorCategory(StrEnum):
18
+ """Categories of sync errors for classification and reporting."""
19
+
20
+ # Dependency/relationship errors
21
+ MILESTONE_NOT_FOUND = "milestone_not_found"
22
+ PROJECT_NOT_FOUND = "project_not_found"
23
+ DEPENDENCY_MISSING = "dependency_missing"
24
+ FOREIGN_KEY_CONSTRAINT = "foreign_key_constraint"
25
+
26
+ # API/network errors
27
+ API_RATE_LIMIT = "api_rate_limit"
28
+ NETWORK_ERROR = "network_error"
29
+ TIMEOUT = "timeout"
30
+ SERVICE_UNAVAILABLE = "service_unavailable"
31
+
32
+ # Authentication/authorization errors
33
+ AUTHENTICATION_FAILED = "authentication_failed"
34
+ PERMISSION_DENIED = "permission_denied"
35
+ TOKEN_EXPIRED = "token_expired"
36
+
37
+ # Data/validation errors
38
+ INVALID_DATA = "invalid_data"
39
+ SCHEMA_MISMATCH = "schema_mismatch"
40
+ DUPLICATE_ENTITY = "duplicate_entity"
41
+
42
+ # Resource errors
43
+ RESOURCE_DELETED = "resource_deleted"
44
+ RESOURCE_NOT_FOUND = "resource_not_found"
45
+
46
+ # System errors
47
+ DATABASE_ERROR = "database_error"
48
+ FILE_SYSTEM_ERROR = "file_system_error"
49
+
50
+ # Unknown/other
51
+ UNKNOWN_ERROR = "unknown_error"
52
+
53
+
54
+ @dataclass
55
+ class ErrorDetails:
56
+ """Detailed information about a specific error occurrence."""
57
+
58
+ category: ErrorCategory
59
+ error_message: str
60
+ error_type: str
61
+ entity_type: str # "Issue", "Milestone", "Project"
62
+ entity_id: str | None = None
63
+ suggested_fix: str | None = None
64
+ is_recoverable: bool = True
65
+ metadata: dict[str, Any] | None = None
66
+
67
+
68
+ @dataclass
69
+ class ErrorSummary:
70
+ """Aggregated summary of errors by category."""
71
+
72
+ category: ErrorCategory
73
+ count: int
74
+ sample_messages: list[str] # Top 3-5 unique error messages
75
+ affected_entities: list[str] # Sample of affected entity IDs
76
+ suggested_fix: str
77
+ is_recoverable: bool
78
+
79
+
80
+ class ErrorClassifier:
81
+ """Classifies and aggregates sync errors for user presentation."""
82
+
83
+ def __init__(self):
84
+ """Initialize error classifier with empty state."""
85
+ self._errors: list[ErrorDetails] = []
86
+ self._category_counts: dict[ErrorCategory, int] = defaultdict(int)
87
+
88
+ def classify_error(
89
+ self,
90
+ error_message: str,
91
+ error_type: str,
92
+ entity_type: str,
93
+ entity_id: str | None = None,
94
+ metadata: dict[str, Any] | None = None,
95
+ ) -> ErrorDetails:
96
+ """Classify an error and determine its category.
97
+
98
+ Args:
99
+ error_message: The error message text
100
+ error_type: The exception type (e.g., "IntegrityError", "ValueError")
101
+ entity_type: Type of entity affected (Issue, Milestone, Project)
102
+ entity_id: Identifier of affected entity
103
+ metadata: Additional context about the error
104
+
105
+ Returns:
106
+ ErrorDetails with classified category and suggested fix
107
+ """
108
+ category = self._determine_category(error_message, error_type, metadata)
109
+ suggested_fix = self._get_suggested_fix(category, error_message)
110
+ is_recoverable = self._is_recoverable(category)
111
+
112
+ error_details = ErrorDetails(
113
+ category=category,
114
+ error_message=error_message,
115
+ error_type=error_type,
116
+ entity_type=entity_type,
117
+ entity_id=entity_id,
118
+ suggested_fix=suggested_fix,
119
+ is_recoverable=is_recoverable,
120
+ metadata=metadata,
121
+ )
122
+
123
+ self._errors.append(error_details)
124
+ self._category_counts[category] += 1
125
+
126
+ return error_details
127
+
128
+ def _determine_category(
129
+ self, error_message: str, error_type: str, metadata: dict[str, Any] | None
130
+ ) -> ErrorCategory:
131
+ """Determine error category from message and type.
132
+
133
+ Args:
134
+ error_message: Error message text
135
+ error_type: Exception type name
136
+ metadata: Additional context
137
+
138
+ Returns:
139
+ Appropriate ErrorCategory
140
+ """
141
+ msg_lower = error_message.lower()
142
+ type_lower = error_type.lower()
143
+
144
+ # Check categories in order of specificity
145
+ category = (
146
+ self._check_dependency_errors(msg_lower)
147
+ or self._check_api_errors(msg_lower)
148
+ or self._check_auth_errors(msg_lower)
149
+ or self._check_data_errors(msg_lower, type_lower)
150
+ or self._check_resource_errors(msg_lower)
151
+ or self._check_file_system_errors(type_lower)
152
+ )
153
+ return category or ErrorCategory.UNKNOWN_ERROR
154
+
155
+ def _check_dependency_errors(self, msg_lower: str) -> ErrorCategory | None:
156
+ """Check for dependency-related errors."""
157
+ if "foreign key constraint" in msg_lower:
158
+ return ErrorCategory.FOREIGN_KEY_CONSTRAINT
159
+ if "milestone not found" in msg_lower or "no such milestone" in msg_lower:
160
+ return ErrorCategory.MILESTONE_NOT_FOUND
161
+ if "project not found" in msg_lower or "no such project" in msg_lower:
162
+ return ErrorCategory.PROJECT_NOT_FOUND
163
+ if "dependency" in msg_lower and (
164
+ "missing" in msg_lower or "not found" in msg_lower
165
+ ):
166
+ return ErrorCategory.DEPENDENCY_MISSING
167
+ return None
168
+
169
+ def _check_api_errors(self, msg_lower: str) -> ErrorCategory | None:
170
+ """Check for API and network errors."""
171
+ if "rate limit" in msg_lower or "429" in msg_lower:
172
+ return ErrorCategory.API_RATE_LIMIT
173
+ if any(x in msg_lower for x in ["connection", "network", "unreachable"]):
174
+ return ErrorCategory.NETWORK_ERROR
175
+ if "timeout" in msg_lower or "timed out" in msg_lower:
176
+ return ErrorCategory.TIMEOUT
177
+ if "503" in msg_lower or "service unavailable" in msg_lower:
178
+ return ErrorCategory.SERVICE_UNAVAILABLE
179
+ return None
180
+
181
+ def _check_auth_errors(self, msg_lower: str) -> ErrorCategory | None:
182
+ """Check for authentication and authorization errors."""
183
+ if any(x in msg_lower for x in ["authentication", "unauthorized", "401"]):
184
+ return ErrorCategory.AUTHENTICATION_FAILED
185
+ if (
186
+ "permission denied" in msg_lower
187
+ or "forbidden" in msg_lower
188
+ or "403" in msg_lower
189
+ ):
190
+ return ErrorCategory.PERMISSION_DENIED
191
+ if "token expired" in msg_lower or "invalid token" in msg_lower:
192
+ return ErrorCategory.TOKEN_EXPIRED
193
+ return None
194
+
195
+ def _check_data_errors(
196
+ self, msg_lower: str, type_lower: str
197
+ ) -> ErrorCategory | None:
198
+ """Check for data integrity errors."""
199
+ if "integrityerror" in type_lower or "constraint" in msg_lower:
200
+ return ErrorCategory.DATABASE_ERROR
201
+ if "validation" in msg_lower or "invalid" in msg_lower:
202
+ return ErrorCategory.INVALID_DATA
203
+ if "duplicate" in msg_lower:
204
+ return ErrorCategory.DUPLICATE_ENTITY
205
+ return None
206
+
207
+ def _check_resource_errors(self, msg_lower: str) -> ErrorCategory | None:
208
+ """Check for resource-related errors."""
209
+ if "deleted" in msg_lower or "removed" in msg_lower:
210
+ return ErrorCategory.RESOURCE_DELETED
211
+ if "not found" in msg_lower or "404" in msg_lower:
212
+ return ErrorCategory.RESOURCE_NOT_FOUND
213
+ return None
214
+
215
+ def _check_file_system_errors(self, type_lower: str) -> ErrorCategory | None:
216
+ """Check for file system errors."""
217
+ if any(x in type_lower for x in ["oserror", "ioerror", "filenotfound"]):
218
+ return ErrorCategory.FILE_SYSTEM_ERROR
219
+ return None
220
+
221
+ def _get_suggested_fix(self, category: ErrorCategory, error_message: str) -> str:
222
+ """Get actionable suggestion for fixing error category.
223
+
224
+ Args:
225
+ category: Error category
226
+ error_message: Original error message
227
+
228
+ Returns:
229
+ Human-readable suggestion
230
+ """
231
+ suggestions = {
232
+ ErrorCategory.MILESTONE_NOT_FOUND: "Run 'roadmap sync' again to pull missing milestones, or manually create the milestone locally",
233
+ ErrorCategory.PROJECT_NOT_FOUND: "Ensure projects are synced before issues. Check GitHub project configuration",
234
+ ErrorCategory.DEPENDENCY_MISSING: "Sync dependent entities first (projects → milestones → issues)",
235
+ ErrorCategory.FOREIGN_KEY_CONSTRAINT: "Run 'roadmap health' to check database integrity. May need to reinitialize database",
236
+ ErrorCategory.API_RATE_LIMIT: "Wait for rate limit to reset (usually 1 hour), or use authenticated requests",
237
+ ErrorCategory.NETWORK_ERROR: "Check internet connection and GitHub service status at https://githubstatus.com",
238
+ ErrorCategory.TIMEOUT: "Retry the operation. Consider syncing in smaller batches",
239
+ ErrorCategory.SERVICE_UNAVAILABLE: "GitHub is experiencing issues. Check https://githubstatus.com and retry later",
240
+ ErrorCategory.AUTHENTICATION_FAILED: "Run 'roadmap git setup --auth' to reconfigure GitHub token",
241
+ ErrorCategory.PERMISSION_DENIED: "Verify GitHub token has required permissions (repo, read:org, read:user)",
242
+ ErrorCategory.TOKEN_EXPIRED: "Generate new GitHub token and update with 'roadmap git setup --auth'",
243
+ ErrorCategory.INVALID_DATA: "Check data format. May need to update roadmap CLI to latest version",
244
+ ErrorCategory.SCHEMA_MISMATCH: "Run 'roadmap health fix --fix-type data_integrity' to repair schema issues",
245
+ ErrorCategory.DUPLICATE_ENTITY: "Run 'roadmap health fix --fix-type duplicate_issues' to resolve duplicates",
246
+ ErrorCategory.RESOURCE_DELETED: "Entity was deleted on remote. This is expected and safe to ignore",
247
+ ErrorCategory.RESOURCE_NOT_FOUND: "Entity may have been renamed or deleted. Check GitHub repository",
248
+ ErrorCategory.DATABASE_ERROR: "Run 'roadmap health' and 'roadmap health fix' to repair database",
249
+ ErrorCategory.FILE_SYSTEM_ERROR: "Check file permissions and available disk space",
250
+ ErrorCategory.UNKNOWN_ERROR: "Run with --verbose for more details. Consider reporting as a bug",
251
+ }
252
+ return suggestions.get(category, "Check logs for more details")
253
+
254
+ def _is_recoverable(self, category: ErrorCategory) -> bool:
255
+ """Determine if error category is typically recoverable.
256
+
257
+ Args:
258
+ category: Error category
259
+
260
+ Returns:
261
+ True if error can typically be recovered from
262
+ """
263
+ non_recoverable = {
264
+ ErrorCategory.AUTHENTICATION_FAILED,
265
+ ErrorCategory.TOKEN_EXPIRED,
266
+ ErrorCategory.PERMISSION_DENIED,
267
+ ErrorCategory.SCHEMA_MISMATCH,
268
+ }
269
+ return category not in non_recoverable
270
+
271
+ def get_summary(self, max_samples: int = 5) -> list[ErrorSummary]:
272
+ """Get aggregated error summary by category.
273
+
274
+ Args:
275
+ max_samples: Maximum number of sample messages/entities per category
276
+
277
+ Returns:
278
+ List of ErrorSummary objects, sorted by count (descending)
279
+ """
280
+ summaries = []
281
+
282
+ for category, count in self._category_counts.items():
283
+ # Get errors for this category
284
+ category_errors = [e for e in self._errors if e.category == category]
285
+
286
+ # Extract unique sample messages (top N)
287
+ messages = list(dict.fromkeys([e.error_message for e in category_errors]))
288
+ sample_messages = messages[:max_samples]
289
+
290
+ # Extract sample affected entities
291
+ entities = [e.entity_id for e in category_errors if e.entity_id]
292
+ affected_entities = list(dict.fromkeys(entities))[:max_samples]
293
+
294
+ # Get suggested fix (same for all in category)
295
+ suggested_fix = (
296
+ category_errors[0].suggested_fix
297
+ if category_errors and category_errors[0].suggested_fix
298
+ else "Check logs for more details"
299
+ )
300
+
301
+ # Check if recoverable (should be same for all)
302
+ is_recoverable = (
303
+ category_errors[0].is_recoverable if category_errors else True
304
+ )
305
+
306
+ summary = ErrorSummary(
307
+ category=category,
308
+ count=count,
309
+ sample_messages=sample_messages,
310
+ affected_entities=affected_entities,
311
+ suggested_fix=suggested_fix,
312
+ is_recoverable=is_recoverable,
313
+ )
314
+ summaries.append(summary)
315
+
316
+ # Sort by count (most common errors first)
317
+ summaries.sort(key=lambda s: s.count, reverse=True)
318
+ return summaries
319
+
320
+ def get_total_errors(self) -> int:
321
+ """Get total number of errors classified."""
322
+ return len(self._errors)
323
+
324
+ def get_category_count(self, category: ErrorCategory) -> int:
325
+ """Get count of errors in specific category."""
326
+ return self._category_counts.get(category, 0)
327
+
328
+ def clear(self):
329
+ """Clear all collected errors."""
330
+ self._errors.clear()
331
+ self._category_counts.clear()
332
+
333
+ def add_errors(self, errors: dict[str, str]) -> None:
334
+ """Bulk add errors from a dictionary of issue_id -> error_message.
335
+
336
+ Args:
337
+ errors: Dictionary mapping issue IDs to error messages
338
+ """
339
+ for issue_id, error_msg in errors.items():
340
+ self.classify_error(
341
+ error_message=error_msg,
342
+ error_type="SyncError",
343
+ entity_type="Issue",
344
+ entity_id=issue_id,
345
+ )
346
+
347
+ def get_summary_dict(self) -> dict[str, int]:
348
+ """Get summary as a dictionary of category group -> count.
349
+
350
+ Returns:
351
+ Dict with keys like "dependency_errors", "api_errors", etc.
352
+ """
353
+ # Group categories into high-level groups
354
+ category_groups = {
355
+ "dependency_errors": [
356
+ ErrorCategory.MILESTONE_NOT_FOUND,
357
+ ErrorCategory.PROJECT_NOT_FOUND,
358
+ ErrorCategory.DEPENDENCY_MISSING,
359
+ ErrorCategory.FOREIGN_KEY_CONSTRAINT,
360
+ ],
361
+ "api_errors": [
362
+ ErrorCategory.API_RATE_LIMIT,
363
+ ErrorCategory.NETWORK_ERROR,
364
+ ErrorCategory.TIMEOUT,
365
+ ErrorCategory.SERVICE_UNAVAILABLE,
366
+ ],
367
+ "auth_errors": [
368
+ ErrorCategory.AUTHENTICATION_FAILED,
369
+ ErrorCategory.PERMISSION_DENIED,
370
+ ErrorCategory.TOKEN_EXPIRED,
371
+ ],
372
+ "data_errors": [
373
+ ErrorCategory.INVALID_DATA,
374
+ ErrorCategory.SCHEMA_MISMATCH,
375
+ ErrorCategory.DUPLICATE_ENTITY,
376
+ ErrorCategory.DATABASE_ERROR,
377
+ ],
378
+ "resource_errors": [
379
+ ErrorCategory.RESOURCE_DELETED,
380
+ ErrorCategory.RESOURCE_NOT_FOUND,
381
+ ],
382
+ "file_system_errors": [
383
+ ErrorCategory.FILE_SYSTEM_ERROR,
384
+ ],
385
+ "unknown_errors": [
386
+ ErrorCategory.UNKNOWN_ERROR,
387
+ ],
388
+ }
389
+
390
+ # Aggregate counts by group
391
+ result = {}
392
+ for group_name, categories in category_groups.items():
393
+ total = sum(self._category_counts.get(cat, 0) for cat in categories)
394
+ result[group_name] = total
395
+
396
+ return result
397
+
398
+ def get_recommendation(self, category_group: str) -> str:
399
+ """Get recommendation for a category group.
400
+
401
+ Args:
402
+ category_group: High-level category group (e.g., "dependency_errors")
403
+
404
+ Returns:
405
+ Recommendation string
406
+ """
407
+ recommendations = {
408
+ "dependency_errors": "Ensure all dependencies (milestones, projects) are synced first. Run 'roadmap sync' again.",
409
+ "api_errors": "Check GitHub service status at https://githubstatus.com. Retry after a short wait.",
410
+ "auth_errors": "Verify GitHub token with 'roadmap git setup --auth'. Ensure token has required permissions (repo, read:org).",
411
+ "data_errors": "Run 'roadmap health' to check database integrity. May need to run 'roadmap health fix'.",
412
+ "resource_errors": "These resources may have been deleted on GitHub. This is expected after cleanup.",
413
+ "file_system_errors": "Check file permissions and disk space. Ensure .roadmap/ directory is writable.",
414
+ "unknown_errors": "Run with --verbose for detailed error messages. Consider reporting as a bug if persistent.",
415
+ }
416
+ return recommendations.get(category_group, "Check logs for more details")
417
+
418
+ def get_issues_by_category(self, category_group: str) -> list[str]:
419
+ """Get list of issue IDs affected by a category group.
420
+
421
+ Args:
422
+ category_group: High-level category group (e.g., "dependency_errors")
423
+
424
+ Returns:
425
+ List of issue IDs
426
+ """
427
+ # Map group to categories
428
+ category_groups = {
429
+ "dependency_errors": [
430
+ ErrorCategory.MILESTONE_NOT_FOUND,
431
+ ErrorCategory.PROJECT_NOT_FOUND,
432
+ ErrorCategory.DEPENDENCY_MISSING,
433
+ ErrorCategory.FOREIGN_KEY_CONSTRAINT,
434
+ ],
435
+ "api_errors": [
436
+ ErrorCategory.API_RATE_LIMIT,
437
+ ErrorCategory.NETWORK_ERROR,
438
+ ErrorCategory.TIMEOUT,
439
+ ErrorCategory.SERVICE_UNAVAILABLE,
440
+ ],
441
+ "auth_errors": [
442
+ ErrorCategory.AUTHENTICATION_FAILED,
443
+ ErrorCategory.PERMISSION_DENIED,
444
+ ErrorCategory.TOKEN_EXPIRED,
445
+ ],
446
+ "data_errors": [
447
+ ErrorCategory.INVALID_DATA,
448
+ ErrorCategory.SCHEMA_MISMATCH,
449
+ ErrorCategory.DUPLICATE_ENTITY,
450
+ ErrorCategory.DATABASE_ERROR,
451
+ ],
452
+ "resource_errors": [
453
+ ErrorCategory.RESOURCE_DELETED,
454
+ ErrorCategory.RESOURCE_NOT_FOUND,
455
+ ],
456
+ "file_system_errors": [
457
+ ErrorCategory.FILE_SYSTEM_ERROR,
458
+ ],
459
+ "unknown_errors": [
460
+ ErrorCategory.UNKNOWN_ERROR,
461
+ ],
462
+ }
463
+
464
+ categories = category_groups.get(category_group, [])
465
+ issue_ids = [
466
+ e.entity_id
467
+ for e in self._errors
468
+ if e.category in categories and e.entity_id
469
+ ]
470
+ return issue_ids