marvisx-cli 0.1.0__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 (587) hide show
  1. core/api/__init__.py +0 -0
  2. core/api/agents/__init__.py +0 -0
  3. core/api/agents/session_health.py +59 -0
  4. core/api/agents/session_manager.py +206 -0
  5. core/api/bin/marvisx-state-hook.py +182 -0
  6. core/api/config.py +533 -0
  7. core/api/db.py +1516 -0
  8. core/api/dependencies/__init__.py +0 -0
  9. core/api/dependencies/tenant.py +34 -0
  10. core/api/main.py +1641 -0
  11. core/api/mcp/__init__.py +8 -0
  12. core/api/mcp/_adapter.py +184 -0
  13. core/api/mcp/server.py +58 -0
  14. core/api/mcp/tools/__init__.py +59 -0
  15. core/api/mcp/tools/brain.py +599 -0
  16. core/api/mcp/tools/graph.py +380 -0
  17. core/api/mcp/tools/handoffs.py +112 -0
  18. core/api/mcp/tools/ingest.py +326 -0
  19. core/api/mcp/tools/learnings.py +144 -0
  20. core/api/mcp/tools/projects.py +99 -0
  21. core/api/mcp/tools/pull_requests.py +173 -0
  22. core/api/mcp/tools/safety.py +111 -0
  23. core/api/mcp/tools/search.py +79 -0
  24. core/api/mcp/tools/tasks.py +258 -0
  25. core/api/middleware/__init__.py +0 -0
  26. core/api/middleware/tool_call_audit.py +111 -0
  27. core/api/models/__init__.py +346 -0
  28. core/api/models/auth.py +48 -0
  29. core/api/models/brain.py +1006 -0
  30. core/api/models/common.py +76 -0
  31. core/api/models/costs.py +91 -0
  32. core/api/models/graph.py +66 -0
  33. core/api/models/graph_cosmo.py +125 -0
  34. core/api/models/graph_pr_impact.py +257 -0
  35. core/api/models/graph_ux.py +141 -0
  36. core/api/models/inbox.py +230 -0
  37. core/api/models/ingest_keys.py +108 -0
  38. core/api/models/kg.py +41 -0
  39. core/api/models/llm_config.py +56 -0
  40. core/api/models/monitoring.py +234 -0
  41. core/api/models/projects.py +161 -0
  42. core/api/models/search.py +42 -0
  43. core/api/models/sessions.py +322 -0
  44. core/api/models/tasks.py +184 -0
  45. core/api/models/teams.py +63 -0
  46. core/api/models/users.py +184 -0
  47. core/api/observability/__init__.py +0 -0
  48. core/api/observability/tracing.py +92 -0
  49. core/api/paths.py +26 -0
  50. core/api/rate_limit.py +24 -0
  51. core/api/rbac.py +112 -0
  52. core/api/routers/__init__.py +0 -0
  53. core/api/routers/_adapter.py +24 -0
  54. core/api/routers/admin_pr_impact.py +230 -0
  55. core/api/routers/admin_settings.py +147 -0
  56. core/api/routers/agent.py +1079 -0
  57. core/api/routers/agent_tokens.py +276 -0
  58. core/api/routers/app_settings.py +112 -0
  59. core/api/routers/audit.py +89 -0
  60. core/api/routers/auth.py +586 -0
  61. core/api/routers/bench.py +161 -0
  62. core/api/routers/brain.py +881 -0
  63. core/api/routers/brain_directions.py +527 -0
  64. core/api/routers/ci_checks.py +140 -0
  65. core/api/routers/comments.py +273 -0
  66. core/api/routers/costs.py +148 -0
  67. core/api/routers/docs_coverage.py +217 -0
  68. core/api/routers/docs_governance.py +63 -0
  69. core/api/routers/documents.py +318 -0
  70. core/api/routers/files.py +163 -0
  71. core/api/routers/finder.py +987 -0
  72. core/api/routers/graph.py +836 -0
  73. core/api/routers/handoffs.py +156 -0
  74. core/api/routers/inbox.py +496 -0
  75. core/api/routers/ingest_api_keys.py +205 -0
  76. core/api/routers/ingest_triage.py +1227 -0
  77. core/api/routers/judge.py +306 -0
  78. core/api/routers/kg.py +336 -0
  79. core/api/routers/learnings.py +253 -0
  80. core/api/routers/llm_config.py +130 -0
  81. core/api/routers/monitoring.py +347 -0
  82. core/api/routers/notifications.py +125 -0
  83. core/api/routers/pr_impact.py +315 -0
  84. core/api/routers/projects.py +1061 -0
  85. core/api/routers/pull_requests.py +312 -0
  86. core/api/routers/push.py +67 -0
  87. core/api/routers/raci.py +228 -0
  88. core/api/routers/search.py +125 -0
  89. core/api/routers/sessions.py +3100 -0
  90. core/api/routers/settings.py +90 -0
  91. core/api/routers/share_repo.py +68 -0
  92. core/api/routers/status_updates.py +96 -0
  93. core/api/routers/tags.py +45 -0
  94. core/api/routers/tasks.py +526 -0
  95. core/api/routers/teams.py +425 -0
  96. core/api/routers/terminal.py +105 -0
  97. core/api/routers/users.py +331 -0
  98. core/api/routers/webhooks.py +330 -0
  99. core/api/runtime_settings.py +84 -0
  100. core/api/security.py +652 -0
  101. core/api/services/__init__.py +0 -0
  102. core/api/services/audit.py +58 -0
  103. core/api/services/auto_approval.py +82 -0
  104. core/api/services/brain/__init__.py +52 -0
  105. core/api/services/brain/baseline.py +230 -0
  106. core/api/services/brain/capabilities.py +75 -0
  107. core/api/services/brain/cascade_rollup.py +388 -0
  108. core/api/services/brain/compound_bridge.py +215 -0
  109. core/api/services/brain/cycle.py +1242 -0
  110. core/api/services/brain/cycle_snapshot.py +371 -0
  111. core/api/services/brain/digest_collector.py +147 -0
  112. core/api/services/brain/direction.py +421 -0
  113. core/api/services/brain/drift.py +356 -0
  114. core/api/services/brain/drift_router.py +409 -0
  115. core/api/services/brain/edge_metrics.py +79 -0
  116. core/api/services/brain/events_reader.py +222 -0
  117. core/api/services/brain/findings.py +1379 -0
  118. core/api/services/brain/findings_reader.py +1006 -0
  119. core/api/services/brain/jobs.py +733 -0
  120. core/api/services/brain/journal.py +206 -0
  121. core/api/services/brain/knowledge_forms.py +92 -0
  122. core/api/services/brain/llm/__init__.py +37 -0
  123. core/api/services/brain/llm/_runner.py +62 -0
  124. core/api/services/brain/llm/base.py +70 -0
  125. core/api/services/brain/llm/cache.py +99 -0
  126. core/api/services/brain/llm/constants.py +46 -0
  127. core/api/services/brain/llm/direction_alignment.py +289 -0
  128. core/api/services/brain/llm/factory.py +132 -0
  129. core/api/services/brain/llm/finding_reasoning.py +98 -0
  130. core/api/services/brain/llm/finding_summary.py +92 -0
  131. core/api/services/brain/llm/grounding.py +46 -0
  132. core/api/services/brain/llm/journal_polish.py +96 -0
  133. core/api/services/brain/llm/local_gateway.py +426 -0
  134. core/api/services/brain/llm/parsers.py +71 -0
  135. core/api/services/brain/llm/router_glue.py +422 -0
  136. core/api/services/brain/memory_ops.py +1677 -0
  137. core/api/services/brain/models.py +140 -0
  138. core/api/services/brain/owner_hint.py +211 -0
  139. core/api/services/brain/recap.py +307 -0
  140. core/api/services/brain/rules/__init__.py +65 -0
  141. core/api/services/brain/rules/_signals.py +205 -0
  142. core/api/services/brain/rules/dr1_activity_without_status.py +108 -0
  143. core/api/services/brain/rules/dr2_decision_without_adr.py +110 -0
  144. core/api/services/brain/rules/dr3_stale_open_loop.py +127 -0
  145. core/api/services/brain/rules/dr4_docs_governance_drift.py +79 -0
  146. core/api/services/brain/rules/dr5_playbook_changed.py +99 -0
  147. core/api/services/brain/rules/dr6_external_update_unpropagated.py +100 -0
  148. core/api/services/brain/rules/dr7_claimed_decision_gap.py +123 -0
  149. core/api/services/brain/rules/dr8_direction_misalignment.py +230 -0
  150. core/api/services/brain/runs_reader.py +485 -0
  151. core/api/services/brain/scope.py +79 -0
  152. core/api/services/brain/sources/__init__.py +46 -0
  153. core/api/services/brain/sources/base.py +86 -0
  154. core/api/services/brain/sources/git_kg.py +393 -0
  155. core/api/services/brain/sources/handoffs.py +157 -0
  156. core/api/services/brain/sources/ingestor.py +130 -0
  157. core/api/services/brain/sources/learnings.py +121 -0
  158. core/api/services/brain/sources/pir_tasks.py +245 -0
  159. core/api/services/brain/watermarks.py +147 -0
  160. core/api/services/brain/ws_emitter.py +170 -0
  161. core/api/services/cc_tasks_reader.py +76 -0
  162. core/api/services/ci_service.py +263 -0
  163. core/api/services/claude_metrics.py +796 -0
  164. core/api/services/codex_metrics.py +364 -0
  165. core/api/services/conversation_reader.py +102 -0
  166. core/api/services/cost_service.py +243 -0
  167. core/api/services/crypto.py +147 -0
  168. core/api/services/docs_governance/__init__.py +1 -0
  169. core/api/services/docs_governance/confidence.py +230 -0
  170. core/api/services/docs_governance/config.py +87 -0
  171. core/api/services/docs_governance/enrichment.py +65 -0
  172. core/api/services/docs_governance/frontmatter_validator.py +83 -0
  173. core/api/services/docs_governance/hard_gates.py +221 -0
  174. core/api/services/docs_governance/triage_orchestrator.py +98 -0
  175. core/api/services/embedding_internal.py +395 -0
  176. core/api/services/embedding_service.py +832 -0
  177. core/api/services/event_dispatcher.py +167 -0
  178. core/api/services/events.py +70 -0
  179. core/api/services/git_ops.py +621 -0
  180. core/api/services/graph_cosmo_service.py +440 -0
  181. core/api/services/graph_ranker.py +306 -0
  182. core/api/services/graph_service.py +1589 -0
  183. core/api/services/inbox.py +800 -0
  184. core/api/services/inbox_digest.py +221 -0
  185. core/api/services/inbox_digest_deep_research.py +80 -0
  186. core/api/services/inbox_digest_jobs.py +595 -0
  187. core/api/services/inbox_gmail_sync.py +167 -0
  188. core/api/services/inbox_llm_classifier.py +906 -0
  189. core/api/services/inbox_source_identity.py +116 -0
  190. core/api/services/inbox_sources.py +456 -0
  191. core/api/services/inbox_taxonomy.py +195 -0
  192. core/api/services/inbox_tldr.py +1079 -0
  193. core/api/services/inbox_triage.py +899 -0
  194. core/api/services/ingest/__init__.py +13 -0
  195. core/api/services/ingest/api_key_auth.py +136 -0
  196. core/api/services/ingest/auto_approve.py +120 -0
  197. core/api/services/ingest/classifier.py +138 -0
  198. core/api/services/ingest/confidence.py +173 -0
  199. core/api/services/ingest/dispatch.py +88 -0
  200. core/api/services/ingest/embedding_router.py +272 -0
  201. core/api/services/ingest/events.py +33 -0
  202. core/api/services/ingest/ignore_patterns.py +79 -0
  203. core/api/services/ingest/image_probe.py +218 -0
  204. core/api/services/ingest/ingress.py +263 -0
  205. core/api/services/ingest/insert_saga.py +793 -0
  206. core/api/services/ingest/llm/__init__.py +13 -0
  207. core/api/services/ingest/llm/anthropic_haiku.py +23 -0
  208. core/api/services/ingest/llm/base.py +59 -0
  209. core/api/services/ingest/llm/byok_provider.py +130 -0
  210. core/api/services/ingest/llm/classification_context.py +301 -0
  211. core/api/services/ingest/llm/config_store.py +246 -0
  212. core/api/services/ingest/llm/factory.py +24 -0
  213. core/api/services/ingest/llm/kg_enricher.py +306 -0
  214. core/api/services/ingest/llm/local_gateway.py +821 -0
  215. core/api/services/ingest/llm/local_vllm.py +23 -0
  216. core/api/services/ingest/llm/openai_nano.py +349 -0
  217. core/api/services/ingest/lock_advisory.py +57 -0
  218. core/api/services/ingest/parser_router.py +1756 -0
  219. core/api/services/ingest/parsers/__init__.py +1 -0
  220. core/api/services/ingest/parsers/docling_parser.py +142 -0
  221. core/api/services/ingest/parsers/docparse_gateway.py +178 -0
  222. core/api/services/ingest/parsers/docx_parser.py +127 -0
  223. core/api/services/ingest/parsers/folder_unpacker.py +85 -0
  224. core/api/services/ingest/parsers/gateway_aux.py +147 -0
  225. core/api/services/ingest/parsers/image_parser.py +251 -0
  226. core/api/services/ingest/parsers/internal_markdown.py +89 -0
  227. core/api/services/ingest/parsers/ocr_gateway.py +117 -0
  228. core/api/services/ingest/parsers/ocr_pdf_parser.py +112 -0
  229. core/api/services/ingest/parsers/pdf_types.py +13 -0
  230. core/api/services/ingest/parsers/transcript_parser.py +445 -0
  231. core/api/services/ingest/parsers/vision_gateway.py +186 -0
  232. core/api/services/ingest/parsers/xlsx_parser.py +91 -0
  233. core/api/services/ingest/parsers/zip_unpacker.py +126 -0
  234. core/api/services/ingest/preflight.py +393 -0
  235. core/api/services/ingest/retry_voyage.py +88 -0
  236. core/api/services/ingest/routing_policy.py +307 -0
  237. core/api/services/ingest/serializers/__init__.py +1 -0
  238. core/api/services/ingest/serializers/xlsx_to_markdown.py +80 -0
  239. core/api/services/ingest/skip_log.py +74 -0
  240. core/api/services/ingest/watcher.py +637 -0
  241. core/api/services/kg/__init__.py +0 -0
  242. core/api/services/kg/audit.py +49 -0
  243. core/api/services/kg/hybrid_search.py +691 -0
  244. core/api/services/kg/lens.py +339 -0
  245. core/api/services/kg/pr_impact.py +770 -0
  246. core/api/services/kg/queries.py +152 -0
  247. core/api/services/kg/ranking.py +89 -0
  248. core/api/services/kg/rrf.py +143 -0
  249. core/api/services/kg_watcher_control.py +161 -0
  250. core/api/services/local_llm/__init__.py +19 -0
  251. core/api/services/local_llm/async_client.py +385 -0
  252. core/api/services/local_llm/client.py +173 -0
  253. core/api/services/local_llm/url_validator.py +44 -0
  254. core/api/services/metrics_collector.py +646 -0
  255. core/api/services/metrics_providers.py +65 -0
  256. core/api/services/model_registry.py +266 -0
  257. core/api/services/model_router.py +137 -0
  258. core/api/services/n8n_client.py +77 -0
  259. core/api/services/newsletter_llm_gateway.py +66 -0
  260. core/api/services/notification_service.py +134 -0
  261. core/api/services/openai_responses.py +55 -0
  262. core/api/services/opencode_metrics.py +375 -0
  263. core/api/services/opencode_sessions.py +173 -0
  264. core/api/services/pii_redactor.py +138 -0
  265. core/api/services/pr_impact_pipeline/__init__.py +21 -0
  266. core/api/services/pr_impact_pipeline/differ.py +421 -0
  267. core/api/services/pr_impact_pipeline/dispatcher.py +415 -0
  268. core/api/services/pr_impact_pipeline/gc.py +93 -0
  269. core/api/services/pr_impact_pipeline/languages.py +192 -0
  270. core/api/services/pr_impact_pipeline/parser.py +178 -0
  271. core/api/services/pr_impact_pipeline/writer.py +394 -0
  272. core/api/services/pr_service.py +1393 -0
  273. core/api/services/project_paths.py +70 -0
  274. core/api/services/project_status_updates.py +265 -0
  275. core/api/services/providers.py +276 -0
  276. core/api/services/push_service.py +170 -0
  277. core/api/services/reminder_service.py +89 -0
  278. core/api/services/runas.py +41 -0
  279. core/api/services/salience_service.py +69 -0
  280. core/api/services/security_collector.py +281 -0
  281. core/api/services/session_catalog.py +385 -0
  282. core/api/services/session_metrics_service.py +301 -0
  283. core/api/services/session_ops.py +272 -0
  284. core/api/services/session_state.py +173 -0
  285. core/api/services/share_links.py +222 -0
  286. core/api/services/task_transitions.py +146 -0
  287. core/api/services/terminal_metrics.py +462 -0
  288. core/api/services/terminal_metrics_dump.py +203 -0
  289. core/api/services/tmux.py +1205 -0
  290. core/api/services/webhook_service.py +422 -0
  291. core/api/services/workspace_sync.py +164 -0
  292. core/api/templates/__init__.py +1 -0
  293. core/api/templates/markdown_share.py +164 -0
  294. core/api/terminal.py +1031 -0
  295. core/api/tests/__init__.py +0 -0
  296. core/api/tests/test_agent_facing_auth_dependencies.py +132 -0
  297. core/api/tests/test_audit_permissions.py +133 -0
  298. core/api/tests/test_backfill_session_conversations.py +90 -0
  299. core/api/tests/test_backfill_working_seconds_msg.py +129 -0
  300. core/api/tests/test_claude_metrics.py +326 -0
  301. core/api/tests/test_codex_metrics.py +189 -0
  302. core/api/tests/test_finder_paths.py +74 -0
  303. core/api/tests/test_git_ops_merge.py +155 -0
  304. core/api/tests/test_learnings_check_search.py +81 -0
  305. core/api/tests/test_metrics_providers.py +133 -0
  306. core/api/tests/test_migration_087.py +164 -0
  307. core/api/tests/test_migration_088.py +94 -0
  308. core/api/tests/test_migration_089.py +116 -0
  309. core/api/tests/test_openai_responses.py +24 -0
  310. core/api/tests/test_opencode_metrics.py +740 -0
  311. core/api/tests/test_opencode_sessions.py +321 -0
  312. core/api/tests/test_pr_workflow_e2e.py +457 -0
  313. core/api/tests/test_projects_handoffs.py +31 -0
  314. core/api/tests/test_providers.py +138 -0
  315. core/api/tests/test_safety_bridge.py +347 -0
  316. core/api/tests/test_session_catalog.py +142 -0
  317. core/api/tests/test_session_conversations.py +512 -0
  318. core/api/tests/test_session_metrics_service.py +270 -0
  319. core/api/tests/test_session_resume_paths.py +548 -0
  320. core/api/tests/test_session_theme_mode_migration.py +56 -0
  321. core/api/tests/test_sessions_rbac.py +131 -0
  322. core/api/tests/test_share_edit.py +398 -0
  323. core/api/tests/test_share_repo.py +200 -0
  324. core/api/tests/test_terminal_session_manager.py +98 -0
  325. core/api/tests/test_terminal_upload.py +34 -0
  326. core/api/tests/test_tmux.py +272 -0
  327. core/api/tests/test_workspace_sync.py +186 -0
  328. core/api/tests/test_ws_ticket_in_memory.py +73 -0
  329. core/api/use_cases/__init__.py +11 -0
  330. core/api/use_cases/_context.py +89 -0
  331. core/api/use_cases/_errors.py +62 -0
  332. core/api/use_cases/_roles.py +16 -0
  333. core/api/use_cases/audit.py +171 -0
  334. core/api/use_cases/brain.py +1232 -0
  335. core/api/use_cases/costs.py +249 -0
  336. core/api/use_cases/graph.py +1153 -0
  337. core/api/use_cases/handoffs.py +506 -0
  338. core/api/use_cases/ingest_triage.py +1229 -0
  339. core/api/use_cases/learnings.py +538 -0
  340. core/api/use_cases/projects.py +705 -0
  341. core/api/use_cases/pull_requests.py +415 -0
  342. core/api/use_cases/search.py +926 -0
  343. core/api/use_cases/tasks.py +1495 -0
  344. core/api/visibility.py +141 -0
  345. core/cli/__init__.py +5 -0
  346. core/cli/_index_source.py +632 -0
  347. core/cli/_runtime_ctx.py +160 -0
  348. core/cli/_transmute.py +241 -0
  349. core/cli/marvis_doctor.py +704 -0
  350. core/cli/marvis_feedback.py +396 -0
  351. core/cli/marvis_governance.py +315 -0
  352. core/cli/marvis_hooks.py +515 -0
  353. core/cli/marvis_init.py +757 -0
  354. core/cli/marvis_mcp.py +401 -0
  355. core/cli/marvis_runtime.py +855 -0
  356. core/cli/marvis_telemetry.py +228 -0
  357. core/scripts/_drift_check.py +716 -0
  358. core/scripts/_frontmatter.py +66 -0
  359. core/scripts/_graph_writer.py +189 -0
  360. core/scripts/ast_parser.py +1553 -0
  361. core/scripts/install_hooks/__init__.py +1 -0
  362. core/scripts/install_hooks/_config.sh +109 -0
  363. core/scripts/install_hooks/block-dangerous-bash.sh +23 -0
  364. core/scripts/install_hooks/block-db-direct-write.sh +23 -0
  365. core/scripts/install_hooks/block-push-no-task.sh +23 -0
  366. core/scripts/install_hooks/block-staging-to-prod.sh +23 -0
  367. core/scripts/install_hooks/block-subtree-push.sh +23 -0
  368. core/scripts/install_hooks/config.json +53 -0
  369. core/scripts/install_hooks/enforce-no-merge-main.sh +23 -0
  370. core/scripts/install_hooks/enforce-worktree.sh +23 -0
  371. core/scripts/install_hooks/quality-gate.sh +170 -0
  372. core/scripts/install_hooks/safety_bridge.py +968 -0
  373. core/scripts/install_hooks/secret-scan.sh +23 -0
  374. core/scripts/migrate_spike_node_ids.py +122 -0
  375. core/scripts/populate_artifacts.py +2198 -0
  376. core/scripts/populate_cross_project.py +2457 -0
  377. core/scripts/populate_inbox_nodes.py +357 -0
  378. core/scripts/populate_pr_impact.py +267 -0
  379. core/scripts/populate_project_nodes.py +603 -0
  380. core/scripts/populate_touch_counter.py +337 -0
  381. core/scripts/reparse_failed.py +57 -0
  382. core/scripts/safety_bridge.py +968 -0
  383. core/telemetry/__init__.py +9 -0
  384. core/telemetry/client.py +405 -0
  385. core/telemetry/schema.py +122 -0
  386. core/wizard/__init__.py +65 -0
  387. core/wizard/byok_vault.py +147 -0
  388. core/wizard/defaults.py +58 -0
  389. core/wizard/state.py +117 -0
  390. core/wizard/steps.py +70 -0
  391. core/wizard/validation.py +136 -0
  392. marvisx_cli-0.1.0.dist-info/METADATA +201 -0
  393. marvisx_cli-0.1.0.dist-info/RECORD +587 -0
  394. marvisx_cli-0.1.0.dist-info/WHEEL +5 -0
  395. marvisx_cli-0.1.0.dist-info/entry_points.txt +3 -0
  396. marvisx_cli-0.1.0.dist-info/licenses/LICENSE +98 -0
  397. marvisx_cli-0.1.0.dist-info/top_level.txt +3 -0
  398. migrations/001_initial.sql +33 -0
  399. migrations/002_tasks.sql +30 -0
  400. migrations/003_session_management.sql +7 -0
  401. migrations/004_projects_comments.sql +65 -0
  402. migrations/005_session_intelligence.sql +15 -0
  403. migrations/006_settings.sql +12 -0
  404. migrations/007_task_scoring.sql +12 -0
  405. migrations/008_cost_tracking.sql +31 -0
  406. migrations/009_session_card_metrics.sql +3 -0
  407. migrations/010_monitoring.sql +55 -0
  408. migrations/012_agent_api.sql +21 -0
  409. migrations/013_session_complete.sql +8 -0
  410. migrations/015_pull_requests.sql +43 -0
  411. migrations/015_pull_requests_down.sql +5 -0
  412. migrations/016_users_raci.sql +116 -0
  413. migrations/017_task_cost_entries.sql +87 -0
  414. migrations/018_agents.sql +73 -0
  415. migrations/018_agents_down.sql +13 -0
  416. migrations/019_review_feedback.sql +18 -0
  417. migrations/020_pr_commit_sha.sql +4 -0
  418. migrations/021_webhook_events.sql +18 -0
  419. migrations/022_devx_agent_managed.sql +11 -0
  420. migrations/022_devx_agent_managed_down.sql +6 -0
  421. migrations/023_devx_p1_gate.sql +7 -0
  422. migrations/023_devx_p1_gate_down.sql +3 -0
  423. migrations/024_chat_messages.sql +16 -0
  424. migrations/024_pr_conversation_id.sql +8 -0
  425. migrations/024_task_indexes.sql +21 -0
  426. migrations/024_task_indexes_down.sql +7 -0
  427. migrations/025_audit_log.sql +17 -0
  428. migrations/026_agent_tokens.sql +20 -0
  429. migrations/027_teams_auth_phase_b.sql +35 -0
  430. migrations/028_learnings.sql +23 -0
  431. migrations/029_team_roles.sql +14 -0
  432. migrations/030_finder_pins.sql +10 -0
  433. migrations/031_pr_deploy_status.sql +9 -0
  434. migrations/032_task_reminders.sql +7 -0
  435. migrations/033_events_retry_count.sql +6 -0
  436. migrations/033_session_owner.sql +9 -0
  437. migrations/034_notifications.sql +38 -0
  438. migrations/035_shared_links.sql +15 -0
  439. migrations/036_session_index_upgrade.sql +29 -0
  440. migrations/037_pr_approval.sql +15 -0
  441. migrations/038_pr_submitted_by.sql +6 -0
  442. migrations/039_push_subscriptions.sql +17 -0
  443. migrations/040_semantic_search.sql +16 -0
  444. migrations/041_workspaces.sql +63 -0
  445. migrations/042_oidc_providers.sql +24 -0
  446. migrations/043_ci_checks.sql +31 -0
  447. migrations/044_agent_metrics.sql +30 -0
  448. migrations/045_documents_doc_type.sql +5 -0
  449. migrations/046_salience.sql +13 -0
  450. migrations/047_seed_missing_agents.sql +6 -0
  451. migrations/048_fix_agent_paths_roles.sql +5 -0
  452. migrations/049_agent_role_and_learnings_schema.sql +3 -0
  453. migrations/050_session_provider.sql +2 -0
  454. migrations/051_session_launch_profile.sql +4 -0
  455. migrations/052_session_theme_mode.sql +2 -0
  456. migrations/052_task_kind.sql +4 -0
  457. migrations/053_inbox_items.sql +31 -0
  458. migrations/054_inbox_triage_contract.sql +30 -0
  459. migrations/055_inbox_topic_treatment.sql +12 -0
  460. migrations/056_inbox_treatment_read_save.sql +57 -0
  461. migrations/057_session_theme_mode_backfill.sql +4 -0
  462. migrations/058_inbox_item_status_lifecycle.sql +13 -0
  463. migrations/059_inbox_tldr_and_source_scores.sql +18 -0
  464. migrations/060_newsletter.sql +16 -0
  465. migrations/061_inbox_redesign.sql +69 -0
  466. migrations/062_fix_inbox_sources_backfill.sql +37 -0
  467. migrations/063_task_completion_mode.sql +23 -0
  468. migrations/064_judge_mode_setting.sql +4 -0
  469. migrations/065_knowledge_graph_spike.sql +40 -0
  470. migrations/066_digest_ranking_inputs.sql +10 -0
  471. migrations/066_kg_artifact_nodes.sql +129 -0
  472. migrations/067_inbox_digest_selections.sql +28 -0
  473. migrations/067_kg_temporal.sql +53 -0
  474. migrations/068_inbox_digest_app_settings.sql +9 -0
  475. migrations/068_kg_touch_counter.sql +52 -0
  476. migrations/069_kg_doc_types.sql +117 -0
  477. migrations/070_digest_ranking_inputs_recovery.sql +3 -0
  478. migrations/071_inbox_digest_selections_recovery.sql +3 -0
  479. migrations/072_inbox_digest_app_settings_recovery.sql +3 -0
  480. migrations/073_kg_cross_project.sql +216 -0
  481. migrations/073_kg_cross_project_down.sql +77 -0
  482. migrations/074_kg_infra_types.sql +208 -0
  483. migrations/074_kg_infra_types_down.sql +80 -0
  484. migrations/075_kg_file_state_recovery.sql +35 -0
  485. migrations/075_kg_file_state_recovery_down.sql +5 -0
  486. migrations/076_kg_watcher_state.sql +33 -0
  487. migrations/076_kg_watcher_state_down.sql +3 -0
  488. migrations/077_kg_doc_types_extend.sql +226 -0
  489. migrations/077_kg_doc_types_extend_down.sql +80 -0
  490. migrations/078_kg_fts5.sql +102 -0
  491. migrations/078_kg_fts5_down.sql +14 -0
  492. migrations/079_kg_missing_indexes.sql +31 -0
  493. migrations/079_kg_missing_indexes_down.sql +10 -0
  494. migrations/080_kg_fts5_extended.sql +232 -0
  495. migrations/080_kg_fts5_extended_down.sql +25 -0
  496. migrations/081_kg_lens_indexes.sql +9 -0
  497. migrations/081_kg_lens_indexes_down.sql +3 -0
  498. migrations/082_kg_pins.sql +26 -0
  499. migrations/082_kg_pins_down.sql +14 -0
  500. migrations/083_kg_graph_nodes_degree.sql +20 -0
  501. migrations/083_kg_graph_nodes_degree_down.sql +15 -0
  502. migrations/084_drop_legacy_scheduler_tables.sql +58 -0
  503. migrations/084_drop_legacy_scheduler_tables_down.sql +112 -0
  504. migrations/085_kg_edge_resolves_to.sql +142 -0
  505. migrations/085_kg_edge_resolves_to_down.sql +66 -0
  506. migrations/086_project_status_updates_feed.sql +20 -0
  507. migrations/086_project_status_updates_feed_down.sql +36 -0
  508. migrations/087_session_metrics_dual.sql +50 -0
  509. migrations/087_session_metrics_dual_down.sql +21 -0
  510. migrations/088_rename_context_pct_legacy.sql +23 -0
  511. migrations/088_rename_context_pct_legacy_down.sql +8 -0
  512. migrations/089_session_metrics_equivalent_cost.sql +26 -0
  513. migrations/089_session_metrics_equivalent_cost_down.sql +11 -0
  514. migrations/090_kg_inbox_node_type.sql +26 -0
  515. migrations/090_kg_inbox_node_type_down.sql +20 -0
  516. migrations/091_kg_inbox_node_type_check.sql +265 -0
  517. migrations/091_kg_inbox_node_type_check_down.sql +129 -0
  518. migrations/092_sessions_activity_state_ts.sql +29 -0
  519. migrations/092_sessions_activity_state_ts_down.sql +14 -0
  520. migrations/093_sessions_activity_state_column.sql +29 -0
  521. migrations/093_sessions_activity_state_column_down.sql +10 -0
  522. migrations/094_ingest_pending.sql +55 -0
  523. migrations/094_ingest_pending_down.sql +15 -0
  524. migrations/095_kg_intent_first.sql +77 -0
  525. migrations/095_kg_intent_first_down.sql +25 -0
  526. migrations/096_kg_xlsx_artifact_prefix.sql +17 -0
  527. migrations/096_kg_xlsx_artifact_prefix_down.sql +11 -0
  528. migrations/097_ingest_change_history.sql +37 -0
  529. migrations/097_ingest_change_history_down.sql +13 -0
  530. migrations/098_kg_node_type_business.sql +254 -0
  531. migrations/098_kg_node_type_business_down.sql +195 -0
  532. migrations/099_kg_edges_restore_weight.sql +58 -0
  533. migrations/099_kg_edges_restore_weight_down.sql +12 -0
  534. migrations/100_kg_enriched_at.sql +25 -0
  535. migrations/100_kg_enriched_at_down.sql +12 -0
  536. migrations/101_local_llm_shadow_comparisons.sql +66 -0
  537. migrations/101_local_llm_shadow_comparisons_down.sql +15 -0
  538. migrations/102_promote_llm_costs.sql +69 -0
  539. migrations/102_promote_llm_costs_down.sql +19 -0
  540. migrations/103_ingest_skipped_log.sql +46 -0
  541. migrations/103_ingest_skipped_log_down.sql +15 -0
  542. migrations/120_docs_governance.sql +50 -0
  543. migrations/120_docs_governance_down.sql +11 -0
  544. migrations/121_notification_event_fk_cleanup.sql +21 -0
  545. migrations/121_notification_event_fk_cleanup_down.sql +10 -0
  546. migrations/122_docs_drift_history.sql +34 -0
  547. migrations/122_docs_drift_history_down.sql +15 -0
  548. migrations/123_ingest_parser_waiting_status.sql +69 -0
  549. migrations/123_ingest_parser_waiting_status_down.sql +69 -0
  550. migrations/124_heypocket_recordings.sql +63 -0
  551. migrations/124_heypocket_recordings_down.sql +13 -0
  552. migrations/125_kg_node_type_record.sql +219 -0
  553. migrations/125_kg_node_type_record_down.sql +205 -0
  554. migrations/126_ingest_terminal_upload_source_kind.sql +69 -0
  555. migrations/126_ingest_terminal_upload_source_kind_down.sql +69 -0
  556. migrations/127_brain_v1_substrate.sql +200 -0
  557. migrations/127_brain_v1_substrate_down.sql +32 -0
  558. migrations/128_brain_drift_signals.sql +157 -0
  559. migrations/128_brain_drift_signals_down.sql +23 -0
  560. migrations/129_brain_memory_operations.sql +232 -0
  561. migrations/129_brain_memory_operations_down.sql +27 -0
  562. migrations/130_brain_findings.sql +258 -0
  563. migrations/130_brain_findings_down.sql +29 -0
  564. migrations/132_kg_pr_modifies.sql +242 -0
  565. migrations/132_kg_pr_modifies_down.sql +99 -0
  566. migrations/133_brain_v1_2_direction_schema.sql +476 -0
  567. migrations/133_brain_v1_2_direction_schema_down.sql +273 -0
  568. migrations/134_brain_journal_narrative_polished.sql +8 -0
  569. migrations/134_brain_journal_narrative_polished_down.sql +6 -0
  570. migrations/135_kg_edges_provider.sql +21 -0
  571. migrations/136_documents_fts.sql +56 -0
  572. migrations/137_promote_llm_costs.sql +59 -0
  573. migrations/137_promote_llm_costs_down.sql +19 -0
  574. migrations/138_ingest_api_keys.sql +39 -0
  575. migrations/138_ingest_api_keys_down.sql +8 -0
  576. migrations/139_ingest_pending_ingress.sql +91 -0
  577. migrations/139_ingest_pending_ingress_down.sql +73 -0
  578. migrations/140_ingest_idempotency_quota.sql +45 -0
  579. migrations/140_ingest_idempotency_quota_down.sql +9 -0
  580. migrations/141_ingest_pending_metadata.sql +16 -0
  581. migrations/141_ingest_pending_metadata_down.sql +7 -0
  582. migrations/142_llm_function_config.sql +36 -0
  583. migrations/142_llm_function_config_down.sql +8 -0
  584. migrations/143_kg_code_embeddings.sql +25 -0
  585. migrations/143_kg_code_embeddings_down.sql +5 -0
  586. migrations/__init__.py +4 -0
  587. projects/_template/project.yaml +46 -0
@@ -0,0 +1,415 @@
1
+ # v1.0.0 - 2026-05-27 - S1 F1.7: pull_requests use_cases extracted from router (merge human-gate)
2
+ """Pull-requests use_cases — pure domain logic, transport-agnostic (no ``fastapi``).
3
+
4
+ Sibling of :mod:`core.api.use_cases.learnings` (the S1 "collapse runtime"
5
+ TEMPLATE). One pure async function per operation, signature
6
+ ``(ctx: CallerContext, db, *typed_args) -> <DTO/dict>``. The HTTP router becomes a
7
+ thin adapter that resolves identity into a :class:`CallerContext`, calls these
8
+ functions, and maps :class:`ServiceError` -> ``HTTPException`` via
9
+ ``routers/_adapter.to_http``. The Python MCP surface (later) calls the SAME
10
+ functions with ``CallerContext.local_single_user()``. One implementation, no fork.
11
+
12
+ How the template decisions land on the PR domain:
13
+
14
+ MERGE HUMAN-GATE — stays a transport-layer ``Depends``, NOT duplicated here.
15
+ The legacy ``POST /{task_id}/merge`` (and ``/revert``) used
16
+ ``Depends(require_role(..., human_only=True))``. ``human_only=True`` swaps the
17
+ auth dependency to ``get_current_user`` (cookie-only), so a Bearer-only agent
18
+ is rejected with ``401 Not authenticated`` BEFORE any handler/``ctx`` exists
19
+ (pinned by the merge-gate regression test ``test_br03_merge_pr_blocked_without_cookie``,
20
+ which asserts 401/403). A use_case receiving a ``ctx`` cannot reproduce that
21
+ 401 — authentication failure happens at the dependency, upstream of the domain.
22
+ So the adapter KEEPS ``Depends(require_role(..., human_only=True))`` and this
23
+ module does NOT add an ``is_human_session`` check on merge/revert: it would be
24
+ dead on both surfaces (HTTP already blocked agents; the MCP/local surface is
25
+ always ``is_human_session=True`` operator). Each write use_case still calls
26
+ :func:`require_role_ctx` so the ROLE-level gate is enforced on the MCP surface
27
+ (parity with the tasks template, which keeps ``require_role_ctx`` despite the
28
+ router's ``Depends(require_role(...))``).
29
+
30
+ DECISION 1 — Visibility on ``get_merge_conflicts``. The legacy endpoint called
31
+ ``check_project_access(project, user, db)`` (raises 404 if the project is not
32
+ visible — does not reveal existence). ``UserInfo.teams`` is NOT carried by
33
+ ``CallerContext``, so the ADAPTER resolves ``visible_projects`` and passes it
34
+ in; this use_case enforces it by raising :class:`NotFoundError` (same 404,
35
+ same ``"Not found"`` shape) when the project is outside the visible set.
36
+ ``visible_projects=None`` means "no restriction" (admin/agent or MCP/local).
37
+
38
+ DECISION 2 — ``deep`` KG enrichment on ``get_pull_request`` is a per-surface
39
+ adapter concern (rate-limit + access log + ``build_kg_context_for_pr``). It
40
+ also depends on ``core.api.services.kg.audit`` which imports ``fastapi`` at
41
+ module top, so it MUST NOT touch this module. ``get_pull_request`` returns the
42
+ PR status dict as-is; the adapter attaches ``kg_context`` when ``deep``.
43
+
44
+ SERVICE IMPORTS ARE FUNCTION-LOCAL (the search.py pattern). ``pr_service`` imports
45
+ ``fastapi.HTTPException`` (a Fase-2 conversion target, OUT OF SCOPE here), so
46
+ importing it at module top would pull ``fastapi`` into ``use_cases`` and break
47
+ the import-linter ``use_cases-no-fastapi`` contract. Every use_case imports
48
+ ``pr_service`` (and ``services.audit.log_audit``) INSIDE the function body.
49
+ ``HTTPException``s raised inside ``pr_service`` (the title/migration/push paths)
50
+ propagate natively over HTTP for now — correct behavior; their conversion to
51
+ ``ServiceError`` is Fase 2 and explicitly not done here.
52
+
53
+ ERROR TRANSLATION. The errors the ROUTER ITSELF raised (catching ``pr_service``'s
54
+ ``ValueError`` / ``PermissionError`` / ``GitOpsError`` / ``MergeConflictError``)
55
+ become domain :class:`ServiceError` subclasses here with the SAME status:
56
+ * ``ValueError`` -> ``ConflictError`` (409) on ``create_branch`` /
57
+ ``NotFoundError`` (404) on ``get_pull_request`` / ``ValidationError`` (422)
58
+ elsewhere — matching each endpoint's original status verbatim.
59
+ * ``PermissionError`` -> ``AuthorizationError`` (403) on approve/request-changes.
60
+ * ``MergeConflictError`` -> ``MergeConflictError`` is re-raised UNCHANGED so
61
+ the adapter can build the exact legacy 409 dict body (``message`` +
62
+ ``conflicting_files``) — ``to_http`` only emits ``{code,message}`` and would
63
+ drop ``conflicting_files``.
64
+ * plain ``GitOpsError`` -> re-raised UNCHANGED so the adapter maps it to the
65
+ legacy ``HTTPException(500, str(exc))`` (pinned by
66
+ ``tests/test_db_contention_task_flows.py::test_merge_push_failure_*`` which
67
+ asserts the raw message text in the 500 body). ``to_http``'s structured
68
+ dict would break that substring assertion.
69
+ ``MergeConflictError`` / ``GitOpsError`` come from ``services.git_ops`` which is
70
+ fastapi-free, so they are imported at module top (safe).
71
+
72
+ ``MergeConflictResponse`` (response DTO) lives in ``core.api.models`` and is NOT
73
+ moved (like costs/tasks). The router keeps the REQUEST models.
74
+ """
75
+ from __future__ import annotations
76
+
77
+ import logging
78
+
79
+ import aiosqlite
80
+
81
+ from core.api.services.git_ops import GitOpsError, MergeConflictError
82
+ from core.api.use_cases._context import CallerContext, require_role_ctx
83
+ from core.api.use_cases._errors import (
84
+ ConflictError,
85
+ NotFoundError,
86
+ ServiceError,
87
+ ValidationError,
88
+ )
89
+
90
+ logger = logging.getLogger(__name__)
91
+
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Use cases — reads
95
+ # ---------------------------------------------------------------------------
96
+
97
+
98
+ async def get_merge_conflicts(
99
+ ctx: CallerContext,
100
+ db: aiosqlite.Connection,
101
+ *,
102
+ project: str,
103
+ visible_projects: set[str] | None = None,
104
+ ) -> list[dict]:
105
+ """Detect migration-number conflicts across open PRs for a project.
106
+
107
+ Any authenticated caller. Visibility (DECISION 1): the adapter resolves
108
+ ``visible_projects``; this use_case raises :class:`NotFoundError` (404, same
109
+ ``"Not found"`` shape as the legacy ``check_project_access``) when ``project``
110
+ is outside the visible set. ``None`` is unrestricted.
111
+
112
+ Raises :class:`ValidationError` (422) on a bad project arg and re-raises
113
+ :class:`GitOpsError` (-> 500 in the adapter) — matching the legacy router.
114
+ """
115
+ if visible_projects is not None and project not in visible_projects:
116
+ raise NotFoundError(code="project_not_found", message="Not found")
117
+
118
+ from core.api.services import pr_service
119
+
120
+ try:
121
+ return await pr_service.get_merge_conflicts(project, db)
122
+ except ValueError as exc:
123
+ raise ValidationError(code="invalid_merge_conflicts_request", message=str(exc))
124
+ # GitOpsError propagates; the adapter maps it to the legacy 500.
125
+
126
+
127
+ async def get_pull_request(
128
+ ctx: CallerContext,
129
+ db: aiosqlite.Connection,
130
+ *,
131
+ task_id: str,
132
+ ) -> dict:
133
+ """Get PR status and diff for a task (any authenticated caller).
134
+
135
+ Returns the ``pr_service.get_pr_status`` dict as-is. The ``deep`` KG context
136
+ is a per-surface adapter concern (DECISION 2) — the adapter attaches it.
137
+ Raises :class:`NotFoundError` (404) when no PR exists for the task.
138
+ """
139
+ from core.api.services import pr_service
140
+
141
+ try:
142
+ return await pr_service.get_pr_status(task_id, db)
143
+ except ValueError as exc:
144
+ raise NotFoundError(code="pr_not_found", message=str(exc))
145
+
146
+
147
+ # ---------------------------------------------------------------------------
148
+ # Use cases — writes (branch/PR lifecycle)
149
+ # ---------------------------------------------------------------------------
150
+
151
+
152
+ async def create_branch(
153
+ ctx: CallerContext,
154
+ db: aiosqlite.Connection,
155
+ *,
156
+ task_id: str,
157
+ ) -> dict:
158
+ """Create git worktree and branch for a task (operator+). Idempotent.
159
+
160
+ Raises :class:`ConflictError` (409) on a ``ValueError`` (matching the legacy
161
+ 409) and re-raises :class:`GitOpsError` (-> 500 in the adapter).
162
+ """
163
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
164
+
165
+ from core.api.services import pr_service
166
+
167
+ try:
168
+ return await pr_service.start_branch_short_write(task_id, db)
169
+ except ValueError as exc:
170
+ raise ConflictError(code="branch_conflict", message=str(exc))
171
+ # GitOpsError propagates; the adapter maps it to the legacy 500.
172
+
173
+
174
+ async def register_branch(
175
+ ctx: CallerContext,
176
+ db: aiosqlite.Connection,
177
+ *,
178
+ task_id: str,
179
+ branch_name: str,
180
+ worktree_path: str | None = None,
181
+ ) -> dict:
182
+ """Register an externally-created branch as a draft PR record (operator+).
183
+
184
+ Idempotent: returns the existing active PR if one already exists. Raises
185
+ :class:`ValidationError` (422) on a ``ValueError`` (matching the legacy 422).
186
+ """
187
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
188
+
189
+ from core.api.services import pr_service
190
+
191
+ try:
192
+ return await pr_service.register_branch(
193
+ task_id, branch_name, db, worktree_path=worktree_path
194
+ )
195
+ except ValueError as exc:
196
+ raise ValidationError(code="register_branch_failed", message=str(exc))
197
+
198
+
199
+ async def submit_pull_request(
200
+ ctx: CallerContext,
201
+ db: aiosqlite.Connection,
202
+ *,
203
+ task_id: str,
204
+ title: str,
205
+ body: str = "",
206
+ ) -> dict:
207
+ """Submit branch for review — moves the PR from draft to open (operator+).
208
+
209
+ ``submitted_by`` is ``ctx.user_id`` (the original router passed
210
+ ``user.user_id``). Raises :class:`ValidationError` (422) on a ``ValueError``
211
+ and re-raises :class:`GitOpsError` (-> 500 in the adapter).
212
+ """
213
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
214
+
215
+ from core.api.services import pr_service
216
+
217
+ try:
218
+ return await pr_service.submit_pr(
219
+ task_id, title, body, db, submitted_by=ctx.user_id
220
+ )
221
+ except ValueError as exc:
222
+ raise ValidationError(code="submit_pr_failed", message=str(exc))
223
+ # GitOpsError propagates; the adapter maps it to the legacy 500.
224
+
225
+
226
+ async def merge_pull_request(
227
+ ctx: CallerContext,
228
+ db: aiosqlite.Connection,
229
+ *,
230
+ task_id: str,
231
+ ) -> dict:
232
+ """Merge a PR (operator+). The HUMAN-ONLY gate is enforced at the HTTP
233
+ ``Depends(require_role(..., human_only=True))`` (see module docstring) — it is
234
+ NOT re-checked here, by design.
235
+
236
+ Writes a ``pr.merge`` audit entry (``merger_id=ctx.user_id``) on success.
237
+ Raises :class:`ValidationError` (422) on a ``ValueError`` and re-raises both
238
+ :class:`MergeConflictError` (-> legacy 409 dict body) and plain
239
+ :class:`GitOpsError` (-> legacy 500 plain body) UNCHANGED so the adapter can
240
+ preserve those exact legacy bodies.
241
+ """
242
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
243
+
244
+ from core.api.services import pr_service
245
+ from core.api.services.audit import log_audit
246
+
247
+ try:
248
+ result = await pr_service.merge_pr(task_id, db, merger_id=ctx.user_id)
249
+ except ValueError as exc:
250
+ raise ValidationError(code="merge_failed", message=str(exc))
251
+ # MergeConflictError (subclass of GitOpsError) and plain GitOpsError propagate;
252
+ # the adapter maps them to the exact legacy 409 dict / 500 plain-string bodies.
253
+
254
+ await log_audit(
255
+ db,
256
+ action="pr.merge",
257
+ user=ctx.username,
258
+ resource_type="pull_request",
259
+ resource_id=task_id,
260
+ details={"task_id": task_id},
261
+ )
262
+ return result
263
+
264
+
265
+ async def approve_pull_request(
266
+ ctx: CallerContext,
267
+ db: aiosqlite.Connection,
268
+ *,
269
+ task_id: str,
270
+ reviewer,
271
+ ) -> dict:
272
+ """Approve a PR (operator+, team_admin of the project or admin+). Four-eyes:
273
+ cannot approve your own PR.
274
+
275
+ ``pr_service.approve_pr`` needs the full ``UserInfo`` (``user_id`` + team
276
+ lookups), which is richer than ``CallerContext``, so the adapter passes the
277
+ resolved ``reviewer`` (the ``UserInfo``) through. Raises
278
+ :class:`ValidationError` (422) on a ``ValueError`` and :class:`AuthorizationError`
279
+ (403) on a ``PermissionError`` — matching the legacy router.
280
+ """
281
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
282
+
283
+ from core.api.services import pr_service
284
+ from core.api.use_cases._errors import AuthorizationError
285
+
286
+ try:
287
+ return await pr_service.approve_pr(task_id, reviewer, db)
288
+ except ValueError as exc:
289
+ raise ValidationError(code="approve_pr_failed", message=str(exc))
290
+ except PermissionError as exc:
291
+ raise AuthorizationError(code="approve_pr_forbidden", message=str(exc))
292
+
293
+
294
+ async def request_pull_request_changes(
295
+ ctx: CallerContext,
296
+ db: aiosqlite.Connection,
297
+ *,
298
+ task_id: str,
299
+ reviewer,
300
+ comment: str,
301
+ ) -> dict:
302
+ """Request changes on a PR (operator+). Revokes approval, sends the task back
303
+ to in_progress.
304
+
305
+ Like :func:`approve_pull_request`, the adapter passes the full ``UserInfo``
306
+ ``reviewer``. Raises :class:`ValidationError` (422) on a ``ValueError`` and
307
+ :class:`AuthorizationError` (403) on a ``PermissionError``.
308
+ """
309
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
310
+
311
+ from core.api.services import pr_service
312
+ from core.api.use_cases._errors import AuthorizationError
313
+
314
+ try:
315
+ return await pr_service.request_changes_pr(task_id, reviewer, comment, db)
316
+ except ValueError as exc:
317
+ raise ValidationError(code="request_changes_failed", message=str(exc))
318
+ except PermissionError as exc:
319
+ raise AuthorizationError(code="request_changes_forbidden", message=str(exc))
320
+
321
+
322
+ async def close_pull_request(
323
+ ctx: CallerContext,
324
+ db: aiosqlite.Connection,
325
+ *,
326
+ task_id: str,
327
+ reason: str = "",
328
+ ) -> dict:
329
+ """Close a PR without merge — the task returns to in_progress (operator+).
330
+
331
+ Raises :class:`ValidationError` (422) on a ``ValueError`` (matching legacy).
332
+ """
333
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
334
+
335
+ from core.api.services import pr_service
336
+
337
+ try:
338
+ return await pr_service.close_pr(task_id, reason, db)
339
+ except ValueError as exc:
340
+ raise ValidationError(code="close_pr_failed", message=str(exc))
341
+
342
+
343
+ async def update_pull_request(
344
+ ctx: CallerContext,
345
+ db: aiosqlite.Connection,
346
+ *,
347
+ task_id: str,
348
+ title: str | None = None,
349
+ body: str | None = None,
350
+ ) -> dict:
351
+ """Update a PR title or body (operator+).
352
+
353
+ Raises :class:`ValidationError` (422) on a ``ValueError`` (matching legacy).
354
+ """
355
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
356
+
357
+ from core.api.services import pr_service
358
+
359
+ try:
360
+ return await pr_service.update_pr(task_id, db, title=title, body=body)
361
+ except ValueError as exc:
362
+ raise ValidationError(code="update_pr_failed", message=str(exc))
363
+
364
+
365
+ async def revert_pull_request(
366
+ ctx: CallerContext,
367
+ db: aiosqlite.Connection,
368
+ *,
369
+ task_id: str,
370
+ ) -> dict:
371
+ """Revert a merged PR — creates a new task + new PR with a git revert commit
372
+ (operator+). HUMAN-ONLY gate at the HTTP ``Depends`` (see module docstring),
373
+ not re-checked here.
374
+
375
+ Writes a ``pr.revert`` audit entry on success. Raises :class:`ValidationError`
376
+ (422) on a ``ValueError`` and re-raises :class:`GitOpsError` (-> 500).
377
+ """
378
+ require_role_ctx(ctx, "operator", "admin", "super_admin")
379
+
380
+ from core.api.services import pr_service
381
+ from core.api.services.audit import log_audit
382
+
383
+ try:
384
+ result = await pr_service.revert_pr(task_id, db)
385
+ except ValueError as exc:
386
+ raise ValidationError(code="revert_pr_failed", message=str(exc))
387
+ # GitOpsError propagates; the adapter maps it to the legacy 500.
388
+
389
+ await log_audit(
390
+ db,
391
+ action="pr.revert",
392
+ user=ctx.username,
393
+ resource_type="pull_request",
394
+ resource_id=task_id,
395
+ details={"task_id": task_id},
396
+ )
397
+ return result
398
+
399
+
400
+ __all__ = [
401
+ "GitOpsError",
402
+ "MergeConflictError",
403
+ "ServiceError",
404
+ "get_merge_conflicts",
405
+ "get_pull_request",
406
+ "create_branch",
407
+ "register_branch",
408
+ "submit_pull_request",
409
+ "merge_pull_request",
410
+ "approve_pull_request",
411
+ "request_pull_request_changes",
412
+ "close_pull_request",
413
+ "update_pull_request",
414
+ "revert_pull_request",
415
+ ]