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,166 @@
1
+ """Health check service for roadmap CLI.
2
+
3
+ This module handles system health checking and reporting, providing
4
+ component status information and overall system health assessment.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from roadmap.common.logging import get_logger
10
+ from roadmap.core.domain.health import HealthStatus
11
+ from roadmap.infrastructure.coordination.core import RoadmapCore
12
+ from roadmap.infrastructure.observability.health import HealthCheck
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class HealthCheckService:
18
+ """Manages system health checks and reporting."""
19
+
20
+ def __init__(self, core: RoadmapCore):
21
+ """Initialize the service.
22
+
23
+ Args:
24
+ core: RoadmapCore instance
25
+ """
26
+ self.core = core
27
+
28
+ def run_all_checks(self) -> dict[str, tuple[HealthStatus, str]]:
29
+ """Run all system health checks.
30
+
31
+ Returns:
32
+ Dictionary mapping check names to (status, message) tuples
33
+ """
34
+ try:
35
+ checks = HealthCheck.run_all_checks(self.core)
36
+ logger.debug("health_checks_completed", check_count=len(checks))
37
+ return checks
38
+ except Exception as e:
39
+ logger.error("health_checks_failed", error=str(e), severity="system_error")
40
+ return {}
41
+
42
+ def get_overall_status(
43
+ self, checks: dict[str, tuple[HealthStatus, str]] | None = None
44
+ ) -> HealthStatus:
45
+ """Get overall system health status.
46
+
47
+ Args:
48
+ checks: Optional dict of checks. If None, runs checks first.
49
+
50
+ Returns:
51
+ Overall HealthStatus
52
+ """
53
+ try:
54
+ if checks is None:
55
+ checks = self.run_all_checks()
56
+
57
+ overall = HealthCheck.get_overall_status(checks)
58
+ logger.debug("overall_status_calculated", status=overall.value)
59
+ return overall
60
+ except Exception as e:
61
+ logger.error("overall_status_failed", error=str(e), severity="system_error")
62
+ return HealthStatus.UNHEALTHY
63
+
64
+ def get_check_status(self, check_name: str) -> tuple[HealthStatus, str] | None:
65
+ """Get status of a specific health check.
66
+
67
+ Args:
68
+ check_name: Name of the check to retrieve
69
+
70
+ Returns:
71
+ Tuple of (status, message) or None if check not found
72
+ """
73
+ try:
74
+ checks = self.run_all_checks()
75
+ return checks.get(check_name)
76
+ except Exception as e:
77
+ logger.error(
78
+ "get_check_status_failed",
79
+ check_name=check_name,
80
+ error=str(e),
81
+ severity="system_error",
82
+ )
83
+ return None
84
+
85
+ def get_health_summary(self) -> dict[str, Any]:
86
+ """Get comprehensive health summary.
87
+
88
+ Returns:
89
+ Dictionary with health summary data
90
+ """
91
+ try:
92
+ checks = self.run_all_checks()
93
+ overall_status = self.get_overall_status(checks)
94
+
95
+ healthy_count = sum(
96
+ 1 for _, (status, _) in checks.items() if status == HealthStatus.HEALTHY
97
+ )
98
+ degraded_count = sum(
99
+ 1
100
+ for _, (status, _) in checks.items()
101
+ if status == HealthStatus.DEGRADED
102
+ )
103
+ unhealthy_count = sum(
104
+ 1
105
+ for _, (status, _) in checks.items()
106
+ if status == HealthStatus.UNHEALTHY
107
+ )
108
+
109
+ return {
110
+ "overall_status": overall_status.value,
111
+ "checks": checks,
112
+ "summary": {
113
+ "total": len(checks),
114
+ "healthy": healthy_count,
115
+ "degraded": degraded_count,
116
+ "unhealthy": unhealthy_count,
117
+ },
118
+ }
119
+ except Exception as e:
120
+ logger.error("health_summary_failed", error=str(e), severity="system_error")
121
+ return {"error": str(e)}
122
+
123
+ def is_healthy(self) -> bool:
124
+ """Check if system is in healthy state.
125
+
126
+ Returns:
127
+ True if overall status is HEALTHY
128
+ """
129
+ try:
130
+ overall_status = self.get_overall_status()
131
+ return overall_status == HealthStatus.HEALTHY
132
+ except Exception as e:
133
+ logger.error(
134
+ "is_healthy_check_failed", error=str(e), severity="system_error"
135
+ )
136
+ return False
137
+
138
+ def is_degraded(self) -> bool:
139
+ """Check if system is in degraded state.
140
+
141
+ Returns:
142
+ True if overall status is DEGRADED
143
+ """
144
+ try:
145
+ overall_status = self.get_overall_status()
146
+ return overall_status == HealthStatus.DEGRADED
147
+ except Exception as e:
148
+ logger.error(
149
+ "is_degraded_check_failed", error=str(e), severity="system_error"
150
+ )
151
+ return False
152
+
153
+ def is_unhealthy(self) -> bool:
154
+ """Check if system is in unhealthy state.
155
+
156
+ Returns:
157
+ True if overall status is UNHEALTHY
158
+ """
159
+ try:
160
+ overall_status = self.get_overall_status()
161
+ return overall_status == HealthStatus.UNHEALTHY
162
+ except Exception as e:
163
+ logger.error(
164
+ "is_unhealthy_check_failed", error=str(e), severity="system_error"
165
+ )
166
+ return True # Fail safe: assume unhealthy on error
@@ -0,0 +1,80 @@
1
+ """Shared health scanning models.
2
+
3
+ Holds dataclasses and enums used by the various health scanners.
4
+ """
5
+
6
+ from dataclasses import dataclass, field
7
+ from enum import StrEnum
8
+ from typing import Any
9
+
10
+
11
+ class EntityType(StrEnum):
12
+ """Types of entities that can be scanned."""
13
+
14
+ ISSUE = "issue"
15
+ MILESTONE = "milestone"
16
+ PROJECT = "project"
17
+
18
+
19
+ class HealthSeverity(StrEnum):
20
+ """Severity levels for health issues."""
21
+
22
+ INFO = "info"
23
+ WARNING = "warning"
24
+ ERROR = "error"
25
+ CRITICAL = "critical"
26
+
27
+
28
+ @dataclass
29
+ class HealthIssue:
30
+ """A single health issue found during scanning."""
31
+
32
+ code: str
33
+ message: str
34
+ severity: HealthSeverity
35
+ category: str
36
+ details: dict[str, Any] = field(default_factory=dict)
37
+
38
+
39
+ @dataclass
40
+ class EntityHealthReport:
41
+ """Health report for a single entity."""
42
+
43
+ entity_id: str
44
+ entity_type: EntityType
45
+ entity_title: str
46
+ status: str
47
+ issues: list[HealthIssue] = field(default_factory=list)
48
+
49
+ @property
50
+ def issue_count(self) -> int:
51
+ """Return the total number of health issues."""
52
+ return len(self.issues)
53
+
54
+ @property
55
+ def error_count(self) -> int:
56
+ """Return the count of error-severity health issues."""
57
+ return sum(1 for i in self.issues if i.severity == HealthSeverity.ERROR)
58
+
59
+ @property
60
+ def warning_count(self) -> int:
61
+ """Return the count of warning-severity health issues."""
62
+ return sum(1 for i in self.issues if i.severity == HealthSeverity.WARNING)
63
+
64
+ @property
65
+ def info_count(self) -> int:
66
+ """Return the count of info-severity health issues."""
67
+ return sum(1 for i in self.issues if i.severity == HealthSeverity.INFO)
68
+
69
+ @property
70
+ def is_healthy(self) -> bool:
71
+ """Check if entity is healthy (no errors or critical issues)."""
72
+ return not any(
73
+ i.severity in (HealthSeverity.ERROR, HealthSeverity.CRITICAL)
74
+ for i in self.issues
75
+ )
76
+
77
+ @property
78
+ def is_degraded(self) -> bool:
79
+ """Check if entity has any warnings."""
80
+ return any(i.severity == HealthSeverity.WARNING for i in self.issues)
@@ -0,0 +1,328 @@
1
+ """Infrastructure validators for health checks.
2
+
3
+ Handles validation of:
4
+ - Roadmap directory structure
5
+ - State database file
6
+ - Issues and milestones directories
7
+ - Git repository status
8
+ - Database integrity
9
+
10
+ Uses BaseValidator abstract class to eliminate boilerplate and ensure
11
+ consistent error handling and logging across all validators.
12
+ """
13
+
14
+ from pathlib import Path
15
+
16
+ import structlog
17
+
18
+ from roadmap.common.logging import get_logger
19
+ from roadmap.common.logging.error_logging import (
20
+ log_error_with_context,
21
+ )
22
+ from roadmap.common.observability.instrumentation import traced
23
+ from roadmap.core.services.validator_base import BaseValidator, HealthStatus
24
+ from roadmap.core.services.validators.health_status_utils import (
25
+ get_overall_status,
26
+ )
27
+ from roadmap.infrastructure.persistence_gateway import PersistenceGateway
28
+
29
+ logger = structlog.get_logger()
30
+
31
+ logger = get_logger(__name__)
32
+
33
+
34
+ class RoadmapDirectoryValidator(BaseValidator):
35
+ """Validator for .roadmap directory."""
36
+
37
+ @staticmethod
38
+ def get_check_name() -> str:
39
+ """Get the name of this check.
40
+
41
+ Returns:
42
+ String identifier for the roadmap_directory check.
43
+ """
44
+ return "roadmap_directory"
45
+
46
+ @staticmethod
47
+ def perform_check() -> tuple[str, str]:
48
+ """Check if .roadmap directory exists and is accessible.
49
+
50
+ Returns:
51
+ Tuple of (status, message) describing the health check result
52
+ """
53
+ roadmap_dir = Path(".roadmap")
54
+ if not roadmap_dir.exists():
55
+ return HealthStatus.DEGRADED, ".roadmap directory not initialized"
56
+
57
+ if not roadmap_dir.is_dir():
58
+ return (
59
+ HealthStatus.UNHEALTHY,
60
+ ".roadmap exists but is not a directory",
61
+ )
62
+
63
+ # Check if directory is writable
64
+ test_file = roadmap_dir / ".health_check"
65
+ try:
66
+ test_file.touch()
67
+ test_file.unlink()
68
+ except OSError as e:
69
+ logger.error(
70
+ "roadmap_dir_not_writable",
71
+ error=str(e),
72
+ action="check_writable",
73
+ severity="system_error",
74
+ )
75
+ return HealthStatus.DEGRADED, ".roadmap directory is not writable"
76
+
77
+ return (
78
+ HealthStatus.HEALTHY,
79
+ ".roadmap directory is accessible and writable",
80
+ )
81
+
82
+
83
+ class StateFileValidator(BaseValidator):
84
+ """Validator for state database file."""
85
+
86
+ @staticmethod
87
+ def get_check_name() -> str:
88
+ """Get the name of this check.
89
+
90
+ Returns:
91
+ String identifier for the state_file check.
92
+ """
93
+ return "state_file"
94
+
95
+ @staticmethod
96
+ def perform_check() -> tuple[str, str]:
97
+ """Check if state database exists and is readable.
98
+
99
+ Returns:
100
+ Tuple of (status, message) describing the health check result
101
+ """
102
+ state_db = Path(".roadmap/db/state.db")
103
+
104
+ if not state_db.exists():
105
+ return (
106
+ HealthStatus.DEGRADED,
107
+ "state.db not found (project not initialized)",
108
+ )
109
+
110
+ # Check if file is readable and has content
111
+ try:
112
+ size = state_db.stat().st_size
113
+ if size == 0:
114
+ return HealthStatus.DEGRADED, "state.db is empty"
115
+
116
+ # Try to open it to verify it's accessible
117
+ with open(state_db, "rb") as f:
118
+ f.read(16) # Read SQLite header
119
+
120
+ except OSError as e:
121
+ return HealthStatus.UNHEALTHY, f"Cannot read state.db: {e}"
122
+
123
+ return HealthStatus.HEALTHY, "state.db is accessible and readable"
124
+
125
+
126
+ class IssuesDirectoryValidator(BaseValidator):
127
+ """Validator for issues directory."""
128
+
129
+ @staticmethod
130
+ def get_check_name() -> str:
131
+ """Get the name of this check.
132
+
133
+ Returns:
134
+ String identifier for the issues_directory check.
135
+ """
136
+ return "issues_directory"
137
+
138
+ @staticmethod
139
+ def perform_check() -> tuple[str, str]:
140
+ """Check if issues directory exists and is accessible.
141
+
142
+ Returns:
143
+ Tuple of (status, message) describing the health check result
144
+ """
145
+ issues_dir = Path(".roadmap/issues")
146
+
147
+ if not issues_dir.exists():
148
+ return HealthStatus.DEGRADED, "issues directory not found"
149
+
150
+ if not issues_dir.is_dir():
151
+ return (
152
+ HealthStatus.UNHEALTHY,
153
+ "issues path exists but is not a directory",
154
+ )
155
+
156
+ # Check if directory is readable
157
+ try:
158
+ list(issues_dir.iterdir())
159
+ except OSError as e:
160
+ return HealthStatus.UNHEALTHY, f"Cannot read issues directory: {e}"
161
+
162
+ return HealthStatus.HEALTHY, "issues directory is accessible"
163
+
164
+
165
+ class MilestonesDirectoryValidator(BaseValidator):
166
+ """Validator for milestones directory."""
167
+
168
+ @staticmethod
169
+ def get_check_name() -> str:
170
+ """Get the name of this check.
171
+
172
+ Returns:
173
+ String identifier for the milestones_directory check.
174
+ """
175
+ return "milestones_directory"
176
+
177
+ @staticmethod
178
+ def perform_check() -> tuple[str, str]:
179
+ """Check if milestones directory exists and is accessible.
180
+
181
+ Returns:
182
+ Tuple of (status, message) describing the health check result
183
+ """
184
+ milestones_dir = Path(".roadmap/milestones")
185
+
186
+ if not milestones_dir.exists():
187
+ return HealthStatus.DEGRADED, "milestones directory not found"
188
+
189
+ if not milestones_dir.is_dir():
190
+ return (
191
+ HealthStatus.UNHEALTHY,
192
+ "milestones path exists but is not a directory",
193
+ )
194
+
195
+ # Check if directory is readable
196
+ try:
197
+ list(milestones_dir.iterdir())
198
+ except OSError as e:
199
+ return (
200
+ HealthStatus.UNHEALTHY,
201
+ f"Cannot read milestones directory: {e}",
202
+ )
203
+
204
+ return HealthStatus.HEALTHY, "milestones directory is accessible"
205
+
206
+
207
+ class GitRepositoryValidator(BaseValidator):
208
+ """Validator for git repository."""
209
+
210
+ @staticmethod
211
+ def get_check_name() -> str:
212
+ """Get the name of this check.
213
+
214
+ Returns:
215
+ String identifier for the git_repository check.
216
+ """
217
+ return "git_repository"
218
+
219
+ @staticmethod
220
+ def perform_check() -> tuple[str, str]:
221
+ """Check if Git repository exists and is accessible.
222
+
223
+ Returns:
224
+ Tuple of (status, message) describing the health check result
225
+ """
226
+ git_dir = Path(".git")
227
+
228
+ if not git_dir.exists():
229
+ return (
230
+ HealthStatus.DEGRADED,
231
+ "not a Git repository (.git not found)",
232
+ )
233
+
234
+ if not git_dir.is_dir():
235
+ return (
236
+ HealthStatus.UNHEALTHY,
237
+ ".git exists but is not a directory",
238
+ )
239
+
240
+ return HealthStatus.HEALTHY, "Git repository is accessible"
241
+
242
+
243
+ class DatabaseIntegrityValidator(BaseValidator):
244
+ """Validator for database integrity."""
245
+
246
+ @staticmethod
247
+ def get_check_name() -> str:
248
+ """Get the name of this check.
249
+
250
+ Returns:
251
+ String identifier for the database_integrity check.
252
+ """
253
+ return "database_integrity"
254
+
255
+ @staticmethod
256
+ def perform_check() -> tuple[str, str]:
257
+ """Check if database is accessible and can be queried.
258
+
259
+ Returns:
260
+ Tuple of (status, message) describing the health check result
261
+ """
262
+ # Import here to avoid circular imports
263
+ try:
264
+ state_mgr = PersistenceGateway.get_state_manager()
265
+ # Try a simple query to verify DB is healthy
266
+ conn = state_mgr._get_connection()
267
+ conn.execute("SELECT 1")
268
+ return HealthStatus.HEALTHY, "Database is accessible and responsive"
269
+ except Exception as e:
270
+ return (
271
+ HealthStatus.UNHEALTHY,
272
+ f"Database query failed: {e}",
273
+ )
274
+
275
+
276
+ class InfrastructureValidator:
277
+ """Orchestrator for infrastructure validation checks."""
278
+
279
+ def __init__(self):
280
+ """Initialize infrastructure validator."""
281
+ # Note: This service uses static methods from validators.
282
+ # Instance creation is kept for potential future stateful functionality.
283
+ pass
284
+
285
+ @traced("run_all_infrastructure_checks")
286
+ def run_all_infrastructure_checks(self) -> dict[str, tuple[str, str]]:
287
+ """Run all infrastructure validators.
288
+
289
+ Returns:
290
+ Dictionary mapping check names to (status, message) tuples
291
+ """
292
+ checks = {}
293
+
294
+ try:
295
+ checks["roadmap_directory"] = RoadmapDirectoryValidator.check()
296
+ checks["state_file"] = StateFileValidator.check()
297
+ checks["issues_directory"] = IssuesDirectoryValidator.check()
298
+ checks["milestones_directory"] = MilestonesDirectoryValidator.check()
299
+ checks["git_repository"] = GitRepositoryValidator.check()
300
+ checks["database_integrity"] = DatabaseIntegrityValidator.check()
301
+
302
+ return checks
303
+ except Exception as e:
304
+ log_error_with_context(
305
+ e,
306
+ operation="run_all_infrastructure_checks",
307
+ entity_type="Infrastructure",
308
+ )
309
+ logger.error(
310
+ "infrastructure_validation_failed",
311
+ error=str(e),
312
+ severity="system_error",
313
+ )
314
+ return {
315
+ "error": (
316
+ HealthStatus.UNHEALTHY,
317
+ f"Infrastructure validation failed: {e}",
318
+ )
319
+ }
320
+
321
+ @traced("get_overall_status")
322
+ def get_overall_status(self, checks: dict) -> str:
323
+ """Get overall status from all checks.
324
+
325
+ Returns:
326
+ Overall status: 'healthy', 'degraded', or 'unhealthy'
327
+ """
328
+ return get_overall_status(checks)