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,219 @@
1
+ """Service for consistent file enumeration and parsing.
2
+
3
+ Consolidates repeated patterns from services:
4
+ - Directory walking with rglob("*.md")
5
+ - Backup file filtering
6
+ - Consistent error handling and logging
7
+ - File-to-object parsing with retry logic
8
+
9
+ This service eliminates ~200 lines of duplicated enumeration logic
10
+ across IssueService, MilestoneService, ProjectService, and validators.
11
+
12
+ Usage:
13
+
14
+ # Simple enumeration and parsing
15
+ issues = FileEnumerationService.enumerate_and_parse(
16
+ directory=issues_dir,
17
+ parser_func=IssueParser.parse_issue_file
18
+ )
19
+
20
+ # Enumeration with filtering
21
+ open_issues = FileEnumerationService.enumerate_with_filter(
22
+ directory=issues_dir,
23
+ parser_func=IssueParser.parse_issue_file,
24
+ filter_func=lambda issue: issue.status == Status.OPEN
25
+ )
26
+
27
+ # Find single item by ID
28
+ issue = FileEnumerationService.find_by_id(
29
+ directory=issues_dir,
30
+ id_value="abc12345",
31
+ parser_func=IssueParser.parse_issue_file
32
+ )
33
+ """
34
+
35
+ from collections.abc import Callable
36
+ from pathlib import Path
37
+ from typing import Any
38
+
39
+ from roadmap.common.errors.error_standards import OperationType, safe_operation
40
+ from roadmap.common.logging import get_logger
41
+
42
+ logger = get_logger(__name__)
43
+
44
+
45
+ class FileEnumerationService:
46
+ """Unified service for enumerating and filtering markdown files.
47
+
48
+ Provides consistent patterns for:
49
+ - Walking directories recursively
50
+ - Filtering backup files
51
+ - Parsing files with error handling
52
+ - Finding items by ID pattern
53
+ """
54
+
55
+ @staticmethod
56
+ @safe_operation(OperationType.READ, "File")
57
+ def enumerate_and_parse(
58
+ directory: Path,
59
+ parser_func: Callable[[Path], Any],
60
+ backup_filter: bool = True,
61
+ warn_on_parse_error: bool = False,
62
+ ) -> list[Any]:
63
+ """Enumerate files and parse with consistent error handling.
64
+
65
+ Args:
66
+ directory: Directory to enumerate
67
+ parser_func: Parser function (e.g., IssueParser.parse_issue_file)
68
+ backup_filter: Skip .backup files if True
69
+ warn_on_parse_error: If True, emit WARNING instead of DEBUG when a
70
+ file fails to parse. Use for entity types where a silent skip
71
+ would cause confusing inconsistencies (e.g., milestones).
72
+
73
+ Returns:
74
+ List of parsed objects (failed items skipped with logging)
75
+ """
76
+ logger.info("enumerate_and_parse_start", directory=str(directory))
77
+ if not directory.exists():
78
+ logger.debug(
79
+ "enumerate_and_parse_skip",
80
+ reason="directory_not_found",
81
+ directory=str(directory),
82
+ )
83
+ return []
84
+
85
+ results = []
86
+ # Use rglob to recursively search subdirectories (e.g., issues organized by milestone)
87
+ for file_path in directory.rglob("*.md"):
88
+ # Filter backup files if requested
89
+ if backup_filter and ".backup" in file_path.name:
90
+ continue
91
+
92
+ try:
93
+ obj = parser_func(file_path)
94
+ # Preserve the file path on the object for later updates
95
+ if obj is not None:
96
+ try:
97
+ obj.file_path = str(file_path)
98
+ except (AttributeError, TypeError):
99
+ # Object doesn't support attribute assignment (e.g., dict)
100
+ # This can happen in tests with mocks
101
+ pass
102
+ results.append(obj)
103
+ except Exception as e:
104
+ log_event = "enumerate_and_parse_skip_file"
105
+ log_kwargs = {
106
+ "file": str(file_path),
107
+ "error": str(e),
108
+ "error_type": type(e).__name__,
109
+ }
110
+ if warn_on_parse_error:
111
+ logger.warning(log_event, **log_kwargs)
112
+ else:
113
+ logger.debug(log_event, **log_kwargs)
114
+ continue
115
+
116
+ logger.debug(
117
+ "enumerate_and_parse_complete",
118
+ directory=str(directory),
119
+ count=len(results),
120
+ )
121
+ return results
122
+
123
+ @staticmethod
124
+ @safe_operation(OperationType.READ, "File")
125
+ def enumerate_with_filter(
126
+ directory: Path,
127
+ parser_func: Callable[[Path], Any],
128
+ filter_func: Callable[[Any], bool],
129
+ warn_on_parse_error: bool = False,
130
+ ) -> list[Any]:
131
+ """Enumerate, parse, and apply filter predicate.
132
+
133
+ Args:
134
+ directory: Directory to enumerate
135
+ parser_func: Parser function (e.g., IssueParser.parse_issue_file)
136
+ filter_func: Predicate function (return True to include)
137
+ warn_on_parse_error: Passed through to enumerate_and_parse.
138
+
139
+ Returns:
140
+ List of parsed and filtered objects
141
+ """
142
+ logger.info(
143
+ "enumerate_with_filter_start",
144
+ directory=str(directory),
145
+ )
146
+ items = FileEnumerationService.enumerate_and_parse(
147
+ directory, parser_func, warn_on_parse_error=warn_on_parse_error
148
+ )
149
+ filtered = [item for item in items if filter_func(item)]
150
+
151
+ logger.debug(
152
+ "enumerate_with_filter_complete",
153
+ directory=str(directory),
154
+ total=len(items),
155
+ filtered=len(filtered),
156
+ )
157
+ return filtered
158
+
159
+ @staticmethod
160
+ @safe_operation(OperationType.READ, "File")
161
+ def find_by_id(
162
+ directory: Path,
163
+ id_value: str,
164
+ parser_func: Callable[[Path], Any],
165
+ ) -> Any | None:
166
+ """Find a single file by ID pattern.
167
+
168
+ Searches for files matching pattern: {id_value}-*.md
169
+ Returns first match or None if not found.
170
+
171
+ Args:
172
+ directory: Directory to search
173
+ id_value: ID to search for (first 8 chars, e.g., "abc12345")
174
+ parser_func: Parser function
175
+
176
+ Returns:
177
+ First matching object or None
178
+ """
179
+ logger.info("find_by_id_start", id=id_value, directory=str(directory))
180
+ if not directory.exists():
181
+ logger.debug(
182
+ "find_by_id_skip",
183
+ reason="directory_not_found",
184
+ id=id_value,
185
+ )
186
+ return None
187
+
188
+ pattern = f"{id_value}-*.md"
189
+ for file_path in directory.rglob(pattern):
190
+ try:
191
+ obj = parser_func(file_path)
192
+ # Preserve the file path on the object for later updates
193
+ if obj is not None:
194
+ try:
195
+ obj.file_path = str(file_path)
196
+ except (AttributeError, TypeError):
197
+ # Object doesn't support attribute assignment
198
+ pass
199
+ logger.debug(
200
+ "find_by_id_found",
201
+ id=id_value,
202
+ file=file_path.name,
203
+ )
204
+ return obj
205
+ except Exception as e:
206
+ logger.debug(
207
+ "find_by_id_parse_failed",
208
+ id=id_value,
209
+ file=file_path.name,
210
+ error=str(e),
211
+ )
212
+ continue
213
+
214
+ logger.debug(
215
+ "find_by_id_not_found",
216
+ id=id_value,
217
+ directory=str(directory),
218
+ )
219
+ return None
@@ -0,0 +1,47 @@
1
+ """GitHub-specific assignee validator implementation.
2
+
3
+ Validates assignees against GitHub repository collaborators and
4
+ provides canonical name resolution.
5
+ """
6
+
7
+ from roadmap.common.logging import get_logger
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ class GitHubAssigneeValidator:
13
+ """Validates assignees against GitHub repository configuration.
14
+
15
+ Delegates to GitHubIntegrationService for the actual validation logic,
16
+ while implementing the AssigneeValidator protocol.
17
+ """
18
+
19
+ def __init__(self, github_service):
20
+ """Initialize with a GitHub integration service.
21
+
22
+ Args:
23
+ github_service: GitHubIntegrationService instance for validation
24
+ """
25
+ self.github_service = github_service
26
+
27
+ def validate(self, assignee: str) -> tuple[bool, str]:
28
+ """Validate assignee against GitHub.
29
+
30
+ Args:
31
+ assignee: Username to validate
32
+
33
+ Returns:
34
+ Tuple of (is_valid, error_message)
35
+ """
36
+ return self.github_service.validate_assignee(assignee)
37
+
38
+ def get_canonical_assignee(self, assignee: str) -> str:
39
+ """Get the canonical form of an assignee name from GitHub.
40
+
41
+ Args:
42
+ assignee: Input assignee name
43
+
44
+ Returns:
45
+ Canonical assignee name
46
+ """
47
+ return self.github_service.get_canonical_assignee(assignee)
@@ -0,0 +1,101 @@
1
+ """GitHub-specific assignee validation implementation.
2
+
3
+ Provides concrete GitHub-based validator implementation without coupling
4
+ core services directly to GitHub adapter.
5
+ """
6
+
7
+ from roadmap.common.logging import get_logger
8
+ from roadmap.core.services.issue.assignee_validation_service import (
9
+ AssigneeValidationResult,
10
+ )
11
+ from roadmap.infrastructure.validation_gateway import ValidationGateway
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class GitHubAssigneeValidator:
17
+ """Validates assignees against GitHub repository access.
18
+
19
+ This is the infrastructure implementation that can be injected
20
+ into core validation services.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ token: str,
26
+ owner: str,
27
+ repo: str,
28
+ cached_members: set[str] | list[str] | None = None,
29
+ ):
30
+ """Initialize with GitHub configuration.
31
+
32
+ Args:
33
+ token: GitHub API token
34
+ owner: Repository owner
35
+ repo: Repository name
36
+ cached_members: Optional cached team members for performance
37
+ """
38
+ self.token = token
39
+ self.owner = owner
40
+ self.repo = repo
41
+ self.cached_members = (
42
+ set(cached_members)
43
+ if isinstance(cached_members, list)
44
+ else (cached_members or set())
45
+ )
46
+
47
+ def validate(self, assignee: str) -> AssigneeValidationResult:
48
+ """Validate assignee against GitHub repository.
49
+
50
+ Args:
51
+ assignee: Username to validate
52
+
53
+ Returns:
54
+ AssigneeValidationResult with validation status
55
+ """
56
+ # Check cache first
57
+ if self.cached_members and assignee in self.cached_members:
58
+ return AssigneeValidationResult(is_valid=True, canonical_id=assignee)
59
+
60
+ # Do full validation via API
61
+ try:
62
+ client = ValidationGateway.get_github_client(
63
+ token=self.token, org=self.owner
64
+ )
65
+ github_valid, github_error = client.validate_assignee(assignee) # type: ignore[attr-defined]
66
+
67
+ if github_valid:
68
+ return AssigneeValidationResult(is_valid=True, canonical_id=assignee)
69
+ else:
70
+ return AssigneeValidationResult(is_valid=False, message=github_error)
71
+
72
+ except Exception as e:
73
+ error_msg = f"GitHub validation failed: {e}"
74
+ logger.warning(
75
+ "github_validation_error",
76
+ assignee=assignee,
77
+ error=str(e),
78
+ severity="infrastructure",
79
+ )
80
+ return AssigneeValidationResult(is_valid=False, message=error_msg)
81
+
82
+ def get_team_members(self) -> list[str]:
83
+ """Get list of valid team members from GitHub.
84
+
85
+ Returns:
86
+ List of GitHub repository team members
87
+ """
88
+ try:
89
+ client = ValidationGateway.get_github_client(
90
+ token=self.token, org=self.owner
91
+ )
92
+ members = client.get_team_members() # type: ignore[attr-defined]
93
+ logger.debug("github_team_members_retrieved", count=len(members))
94
+ return members
95
+ except Exception as e:
96
+ logger.warning(
97
+ "github_team_members_retrieval_failed",
98
+ error=str(e),
99
+ severity="infrastructure",
100
+ )
101
+ return []
@@ -0,0 +1,96 @@
1
+ """Milestone Consistency Validation Module.
2
+
3
+ Handles validation and fixing of milestone filename and name field consistency.
4
+ Extracted from RoadmapCore to reduce god object complexity.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+
11
+ from roadmap.infrastructure.validation_gateway import ValidationGateway
12
+
13
+
14
+ class MilestoneConsistencyValidator:
15
+ """Validates and fixes milestone naming consistency."""
16
+
17
+ def __init__(self, milestones_dir: Path):
18
+ """Initialize validator with milestones directory.
19
+
20
+ Args:
21
+ milestones_dir: Path to milestones directory
22
+ """
23
+ self.milestones_dir = milestones_dir
24
+
25
+ def validate(self) -> list[dict[str, str]]:
26
+ """Check for inconsistencies between milestone filenames and name fields.
27
+
28
+ Returns:
29
+ List of dictionaries with inconsistency details
30
+ """
31
+ inconsistencies = []
32
+
33
+ for milestone_file in sorted(self.milestones_dir.rglob("*.md"), key=str):
34
+ try:
35
+ milestone = ValidationGateway.parse_milestone_for_validation(
36
+ milestone_file
37
+ )
38
+ expected_filename = milestone.filename
39
+ actual_filename = milestone_file.name
40
+
41
+ if expected_filename != actual_filename:
42
+ inconsistencies.append(
43
+ {
44
+ "file": actual_filename,
45
+ "name": milestone.name,
46
+ "expected_filename": expected_filename,
47
+ "type": "filename_mismatch",
48
+ }
49
+ )
50
+ except Exception as e:
51
+ inconsistencies.append(
52
+ {
53
+ "file": milestone_file.name,
54
+ "name": "PARSE_ERROR",
55
+ "expected_filename": "N/A",
56
+ "type": "parse_error",
57
+ "error": str(e),
58
+ }
59
+ )
60
+
61
+ return inconsistencies
62
+
63
+ def fix(self) -> dict[str, list[str]]:
64
+ """Fix milestone filename inconsistencies by renaming files to match name fields.
65
+
66
+ Returns:
67
+ Dictionary with 'renamed' and 'errors' lists
68
+ """
69
+ results = {"renamed": [], "errors": []}
70
+ inconsistencies = self.validate()
71
+
72
+ for issue in inconsistencies:
73
+ if issue["type"] == "filename_mismatch":
74
+ old_path = self.milestones_dir / issue["file"]
75
+ new_path = self.milestones_dir / issue["expected_filename"]
76
+
77
+ try:
78
+ # Check if target filename already exists
79
+ if new_path.exists():
80
+ results["errors"].append(
81
+ f"Cannot rename {issue['file']} -> {issue['expected_filename']}: target exists"
82
+ )
83
+ continue
84
+
85
+ old_path.rename(new_path)
86
+ results["renamed"].append(
87
+ f"{issue['file']} -> {issue['expected_filename']}"
88
+ )
89
+ except Exception as e:
90
+ results["errors"].append(
91
+ f"Failed to rename {issue['file']}: {str(e)}"
92
+ )
93
+ else:
94
+ results["errors"].append(f"Cannot fix {issue['file']}: {issue['type']}")
95
+
96
+ return results
@@ -0,0 +1,37 @@
1
+ """Vanilla/Git assignee validator for git sync backend.
2
+
3
+ Used when the sync backend is vanilla/git, this validator accepts
4
+ any assignee without validation since there's no centralized user registry.
5
+ """
6
+
7
+
8
+ class VanillaAssigneeValidator:
9
+ """Validator that accepts any assignee without validation.
10
+
11
+ Used for git/vanilla sync backend where there's no centralized
12
+ user registry to validate against.
13
+ """
14
+
15
+ def validate(self, assignee: str) -> tuple[bool, str]:
16
+ """Accept any non-empty assignee without validation.
17
+
18
+ Args:
19
+ assignee: Username to validate
20
+
21
+ Returns:
22
+ (True, "") if assignee is non-empty, otherwise (False, error message)
23
+ """
24
+ if not assignee or not assignee.strip():
25
+ return False, "Assignee cannot be empty"
26
+ return True, ""
27
+
28
+ def get_canonical_assignee(self, assignee: str) -> str:
29
+ """Return the assignee as-is (no mapping).
30
+
31
+ Args:
32
+ assignee: Input assignee name
33
+
34
+ Returns:
35
+ Same assignee name unchanged
36
+ """
37
+ return assignee
@@ -0,0 +1,96 @@
1
+ """Gateway to validation infrastructure adapter access for core services.
2
+
3
+ This module mediates core service access to validation-related adapters,
4
+ ensuring proper layer separation between Core and Infrastructure.
5
+
6
+ All imports from roadmap.adapters accessed via validation are localized here.
7
+ Core services use this gateway instead of importing validation modules directly
8
+ or accessing adapters through validation.
9
+ """
10
+
11
+ import os
12
+ from typing import Any
13
+
14
+
15
+ class _NullGitHubClient:
16
+ """Fallback client for validation when no GitHub token is configured."""
17
+
18
+ def validate_assignee(self, assignee: str) -> tuple[bool, str]:
19
+ return False, "GitHub token not configured"
20
+
21
+ def get_team_members(self) -> list[str]:
22
+ return []
23
+
24
+
25
+ class ValidationGateway:
26
+ """Gateway for validation operations.
27
+
28
+ Provides a centralized interface for core services to access validation
29
+ functionality that internally uses adapters (GitHub, persistence parsers, etc.)
30
+ without direct adapter imports in core.
31
+ """
32
+
33
+ @staticmethod
34
+ def get_github_client(token: str | None = None, org: str | None = None) -> Any:
35
+ """Get GitHub API client for validation operations.
36
+
37
+ Args:
38
+ token: GitHub API token (optional)
39
+ org: GitHub organization (optional)
40
+
41
+ Returns:
42
+ GitHub client instance from adapters
43
+ """
44
+ from roadmap.adapters.github.github import GitHubClient
45
+ from roadmap.infrastructure.security.credentials import get_credential_manager
46
+
47
+ resolved_token = token or os.getenv("GITHUB_TOKEN")
48
+ if not resolved_token:
49
+ try:
50
+ resolved_token = get_credential_manager().get_token()
51
+ except Exception:
52
+ resolved_token = None
53
+
54
+ if not resolved_token:
55
+ return _NullGitHubClient()
56
+
57
+ return GitHubClient(token=resolved_token, owner=org)
58
+
59
+ @staticmethod
60
+ def parse_issue_for_validation(file_path: Any) -> Any:
61
+ """Parse issue from file for validation.
62
+
63
+ Args:
64
+ file_path: Path to issue file
65
+
66
+ Returns:
67
+ Parsed Issue object
68
+ """
69
+ from roadmap.adapters.persistence.parser import IssueParser
70
+
71
+ return IssueParser.parse_issue_file(file_path)
72
+
73
+ @staticmethod
74
+ def parse_milestone_for_validation(file_path: Any) -> Any:
75
+ """Parse milestone from file for validation.
76
+
77
+ Args:
78
+ file_path: Path to milestone file
79
+
80
+ Returns:
81
+ Parsed Milestone object
82
+ """
83
+ from roadmap.adapters.persistence.parser import MilestoneParser
84
+
85
+ return MilestoneParser.parse_milestone_file(file_path)
86
+
87
+ @staticmethod
88
+ def get_parser_module() -> Any:
89
+ """Get the parser module for custom parsing operations.
90
+
91
+ Returns:
92
+ Parser module from adapters
93
+ """
94
+ from roadmap.adapters.persistence import parser
95
+
96
+ return parser