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,58 @@
1
+ # v1.0.0 - 2026-03-03 - Audit logging for privileged human operations
2
+ """
3
+ Audit logging service.
4
+
5
+ Writes immutable records to the audit_log table for human-only privileged
6
+ operations: PR merge/revert, task approve/complete, delete operations, user
7
+ management actions.
8
+
9
+ Non-critical: failures are logged as warnings but never block the caller.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import logging
15
+ from typing import Any
16
+
17
+ import aiosqlite
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ async def log_audit(
23
+ db: aiosqlite.Connection,
24
+ action: str,
25
+ user: str,
26
+ resource_type: str,
27
+ resource_id: str,
28
+ details: dict[str, Any] | None = None,
29
+ ) -> None:
30
+ """Write one audit record to the audit_log table.
31
+
32
+ Args:
33
+ db: Active aiosqlite connection (request-scoped or fresh).
34
+ action: Verb describing the privileged operation, e.g.
35
+ "pr.merge", "task.approve", "task.delete", "user.delete".
36
+ user: Username (or agent slug) performing the action.
37
+ resource_type: Entity type: "pull_request", "task", "user", "raci", etc.
38
+ resource_id: Primary key of the affected resource.
39
+ details: Optional free-form dict serialised to JSON for extra context.
40
+
41
+ Non-critical: any exception is caught and logged as a warning so the
42
+ caller's business logic is never interrupted by an audit failure.
43
+ """
44
+ try:
45
+ await db.execute(
46
+ "INSERT INTO audit_log (action, user, resource_type, resource_id, details_json) "
47
+ "VALUES (?, ?, ?, ?, ?)",
48
+ (
49
+ action,
50
+ user,
51
+ resource_type,
52
+ resource_id,
53
+ json.dumps(details) if details is not None else None,
54
+ ),
55
+ )
56
+ await db.commit()
57
+ except Exception as exc: # noqa: BLE001
58
+ logger.warning("log_audit failed (non-critical): %s", exc)
@@ -0,0 +1,82 @@
1
+ # v1.3.0 - 2026-05-28 - Self-improvement veto agent list is config-driven (SELF_IMPROVEMENT_AGENTS)
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+
8
+ from core.api.config import settings
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class ApprovalDecision(str, Enum):
14
+ AUTO_APPROVED = "auto_approved"
15
+ PENDING_HUMAN = "pending"
16
+
17
+
18
+ @dataclass
19
+ class ApprovalPolicy:
20
+ """Evaluate whether a new task should be auto-approved or require human review.
21
+
22
+ Safety valves:
23
+ - Impact veto: high-impact tasks always need human review
24
+ - Confidence floor: low-confidence scores need human review
25
+ - Daily cap: checked externally (not in this class)
26
+ """
27
+
28
+ agent_ease_threshold: int = 6
29
+ hybrid_ease_threshold: int = 7
30
+ impact_veto_threshold: int = 9
31
+ min_confidence: int = 5
32
+
33
+ def evaluate(
34
+ self,
35
+ delegation: str | None,
36
+ ease: int | None,
37
+ impact: int | None,
38
+ confidence: int | None,
39
+ scored_by: str | None,
40
+ created_by: str | None,
41
+ ) -> tuple[ApprovalDecision, str]:
42
+ """Return (decision, reason) for a task's initial status."""
43
+
44
+ # Unscored tasks always need human review
45
+ if delegation is None or ease is None:
46
+ return ApprovalDecision.PENDING_HUMAN, "unscored_task"
47
+
48
+ # Self-improvement hybrid proposals always require human review.
49
+ # The agent list is config-driven (SELF_IMPROVEMENT_AGENTS) so OSS core
50
+ # ships no internal agent names; our deploy .env arms the veto. Read
51
+ # settings live so tests can override via monkeypatch.
52
+ if (
53
+ created_by is not None
54
+ and created_by in settings.self_improvement_agents
55
+ and delegation == "hybrid"
56
+ ):
57
+ return ApprovalDecision.PENDING_HUMAN, "agent_self_improvement_hybrid_veto"
58
+
59
+ # Human delegation = human IS the gate, auto-approve
60
+ if delegation == "human":
61
+ return ApprovalDecision.AUTO_APPROVED, "delegation_human"
62
+
63
+ # Safety valve: high impact always needs human eyes
64
+ if impact is not None and impact >= self.impact_veto_threshold:
65
+ return ApprovalDecision.PENDING_HUMAN, "high_impact_veto"
66
+
67
+ # Safety valve: low confidence = uncertain, needs human
68
+ if confidence is not None and confidence < self.min_confidence:
69
+ return ApprovalDecision.PENDING_HUMAN, "low_confidence"
70
+
71
+ # Threshold-based auto-approval
72
+ if delegation == "agent" and ease >= self.agent_ease_threshold:
73
+ return ApprovalDecision.AUTO_APPROVED, "agent_ease_threshold"
74
+
75
+ if delegation == "hybrid" and ease >= self.hybrid_ease_threshold:
76
+ return ApprovalDecision.AUTO_APPROVED, "hybrid_ease_threshold"
77
+
78
+ return ApprovalDecision.PENDING_HUMAN, "below_threshold"
79
+
80
+
81
+ # Singleton — can be overridden per-project via project.yaml thresholds
82
+ DEFAULT_POLICY = ApprovalPolicy()
@@ -0,0 +1,52 @@
1
+ # Brain v1 — Digest + Journal subpackage (sub-01).
2
+ # Layering: api/models/brain.py owns cross-boundary contracts.
3
+ # Service-private dataclasses live in api/services/brain/models.py.
4
+ # No LLM imports allowed at this layer (parent §9.3 invariant).
5
+ from __future__ import annotations
6
+
7
+ from core.api.services.brain.cycle import (
8
+ canonical_evidence,
9
+ cycle_cutoff_at,
10
+ current_brain_cycle_key,
11
+ derive_event_id,
12
+ evidence_hash as compute_evidence_hash,
13
+ make_event_id,
14
+ persist_event,
15
+ polish_pending_journals,
16
+ polish_run_journals,
17
+ publish_run_journals,
18
+ update_run_status,
19
+ )
20
+ from core.api.services.brain.capabilities import KNOWLEDGE_GLYPHS, get_capabilities
21
+ from core.api.services.brain.events_reader import list_events_for_cycle
22
+ from core.api.services.brain.jobs import recompute_brain_cycle, run_brain_jobs_if_due
23
+ from core.api.services.brain.runs_reader import (
24
+ fetch_single_run,
25
+ get_pipeline_counters,
26
+ list_runs,
27
+ )
28
+ from core.api.services.brain.ws_emitter import emit_phase_complete, get_hub
29
+
30
+ __all__ = [
31
+ "KNOWLEDGE_GLYPHS",
32
+ "canonical_evidence",
33
+ "compute_evidence_hash",
34
+ "current_brain_cycle_key",
35
+ "cycle_cutoff_at",
36
+ "derive_event_id",
37
+ "emit_phase_complete",
38
+ "fetch_single_run",
39
+ "get_capabilities",
40
+ "get_hub",
41
+ "get_pipeline_counters",
42
+ "list_events_for_cycle",
43
+ "list_runs",
44
+ "make_event_id",
45
+ "persist_event",
46
+ "polish_pending_journals",
47
+ "polish_run_journals",
48
+ "publish_run_journals",
49
+ "recompute_brain_cycle",
50
+ "run_brain_jobs_if_due",
51
+ "update_run_status",
52
+ ]
@@ -0,0 +1,230 @@
1
+ # Brain v1 — Direction baseline resolver (sub-02 C2).
2
+ # Priority chain: latest journal entry → latest project_status_update → latest
3
+ # handoff (auto-handoff). Conflict detection across the three sources.
4
+ # Never returns None — uses BaselineReference(source='none', ref=None) sentinel.
5
+ #
6
+ # Reads ONLY from L2 outputs (journal API) and read-only substrate. Drift rules
7
+ # consume the precomputed baselines from CycleSnapshot.baselines.
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from datetime import datetime, timezone
12
+ from typing import Any
13
+
14
+ from core.api.db import acquire_db
15
+ from core.api.models.brain import BaselineReference, DirectionSource, ScopeType
16
+ from core.api.services.brain.journal import get_latest_entry
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def _parse_iso(value: str | None) -> datetime | None:
22
+ if not value:
23
+ return None
24
+ try:
25
+ dt = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
26
+ except ValueError:
27
+ return None
28
+ if dt.tzinfo is None:
29
+ dt = dt.replace(tzinfo=timezone.utc)
30
+ return dt.astimezone(timezone.utc)
31
+
32
+
33
+ async def _resolve_project_direction(
34
+ *, scope_type: ScopeType, scope_key: str, as_of: datetime
35
+ ) -> tuple[str | None, datetime | None]:
36
+ """Return (ref, last_updated_at) of the project_directions row for a slug.
37
+
38
+ Brain v1.2: this is the priority-1 source of baseline truth for project
39
+ scope when present. Falls through silently when the table is empty
40
+ (cold start before bootstrap rollout) or the project lacks a direction.
41
+ """
42
+ if scope_type != "project":
43
+ return (None, None)
44
+ try:
45
+ async with acquire_db() as db:
46
+ row = await (
47
+ await db.execute(
48
+ "SELECT project_slug, last_updated_at FROM project_directions"
49
+ " WHERE project_slug = ?",
50
+ (scope_key,),
51
+ )
52
+ ).fetchone()
53
+ except Exception as exc: # noqa: BLE001 — table may not exist on cold DB
54
+ logger.debug("baseline: project_directions lookup skipped (%s)", exc)
55
+ return (None, None)
56
+ if row is None:
57
+ return (None, None)
58
+ slug = row[0] if not hasattr(row, "keys") else row["project_slug"]
59
+ state_at = row[1] if not hasattr(row, "keys") else row["last_updated_at"]
60
+ return (f"project_directions:{slug}", _parse_iso(state_at))
61
+
62
+
63
+ async def _resolve_status_update(
64
+ *, scope_type: ScopeType, scope_key: str, as_of: datetime, workspace_id: str
65
+ ) -> tuple[str | None, datetime | None]:
66
+ """Return (ref, state_at) of the latest project_status_update for the scope.
67
+
68
+ Restricted to project scope (status updates are project-only). For company
69
+ or program scope returns (None, None) — falls through to handoff.
70
+ """
71
+ if scope_type != "project":
72
+ return (None, None)
73
+ cutoff = as_of.astimezone(timezone.utc).isoformat()
74
+ try:
75
+ async with acquire_db() as db:
76
+ row = await (
77
+ await db.execute(
78
+ "SELECT update_id, created_at FROM project_status_updates "
79
+ "WHERE project = ? AND created_at <= ? "
80
+ "AND COALESCE(derived, 0) = 0 "
81
+ "ORDER BY created_at DESC LIMIT 1",
82
+ (scope_key, cutoff),
83
+ )
84
+ ).fetchone()
85
+ except Exception as exc: # noqa: BLE001 — table may not exist in test DB
86
+ logger.debug("baseline: status update lookup skipped (%s)", exc)
87
+ return (None, None)
88
+ if row is None:
89
+ return (None, None)
90
+ ref = row[0] if not hasattr(row, "keys") else row["update_id"]
91
+ state_at = row[1] if not hasattr(row, "keys") else row["created_at"]
92
+ return (f"project_status_update:{ref}", _parse_iso(state_at))
93
+
94
+
95
+ async def _resolve_handoff(
96
+ *, scope_type: ScopeType, scope_key: str, as_of: datetime, workspace_id: str
97
+ ) -> tuple[str | None, datetime | None]:
98
+ """Return (ref, state_at) of the latest auto_handoff for the scope."""
99
+ cutoff = as_of.astimezone(timezone.utc).isoformat()
100
+ try:
101
+ async with acquire_db() as db:
102
+ row = await (
103
+ await db.execute(
104
+ "SELECT handoff_id, created_at FROM handoffs "
105
+ "WHERE project = ? AND created_at <= ? "
106
+ "AND COALESCE(kind, '') IN ('auto_handoff','handoff') "
107
+ "ORDER BY created_at DESC LIMIT 1",
108
+ (scope_key,) if scope_type == "project" else (scope_key,),
109
+ )
110
+ ).fetchone()
111
+ except Exception as exc: # noqa: BLE001 — table may not exist
112
+ logger.debug("baseline: handoff lookup skipped (%s)", exc)
113
+ return (None, None)
114
+ if row is None:
115
+ return (None, None)
116
+ ref = row[0] if not hasattr(row, "keys") else row["handoff_id"]
117
+ state_at = row[1] if not hasattr(row, "keys") else row["created_at"]
118
+ return (f"handoff:{ref}", _parse_iso(state_at))
119
+
120
+
121
+ def _direction_marker(payload: Any) -> str | None:
122
+ if isinstance(payload, dict):
123
+ marker = payload.get("direction_marker") or payload.get("direction")
124
+ if isinstance(marker, str):
125
+ return marker.strip().lower() or None
126
+ return None
127
+
128
+
129
+ async def resolve_baseline(
130
+ scope_type: ScopeType,
131
+ scope_key: str,
132
+ *,
133
+ cycle_key: str,
134
+ as_of: datetime,
135
+ workspace_id: str = "ws_default",
136
+ visible_projects: set[str] | None = None,
137
+ ) -> BaselineReference:
138
+ """Resolve the expected direction baseline for the scope.
139
+
140
+ Priority chain (sub-02 C2):
141
+ 1. Latest Journal entry within cycle window.
142
+ 2. Latest project status update within 7d (derived=False).
143
+ 3. Latest handoff within 7d (auto_handoff or handoff kind).
144
+
145
+ Returns BaselineReference(source='none') sentinel when nothing matches.
146
+ Conflict detection compares direction markers across resolved sources.
147
+ """
148
+ # Visibility short-circuit: if caller can see only some projects and this
149
+ # one isn't visible, refuse to leak existence.
150
+ if (
151
+ scope_type == "project"
152
+ and visible_projects is not None
153
+ and scope_key not in visible_projects
154
+ ):
155
+ return BaselineReference(source="none", ref=None, confidence=0.7)
156
+
157
+ # Brain v1.2 (2026-05-18): project_directions becomes the priority-1
158
+ # source for project scope. Falls back to journal -> status -> handoff
159
+ # when no direction is present (bootstrap not yet applied).
160
+ direction_ref, direction_state_at = await _resolve_project_direction(
161
+ scope_type=scope_type, scope_key=scope_key, as_of=as_of
162
+ )
163
+
164
+ journal = await get_latest_entry(
165
+ scope_type, scope_key, before=cycle_key, workspace_id=workspace_id
166
+ )
167
+ journal_ref: str | None = None
168
+ journal_state_at: datetime | None = None
169
+ journal_marker: str | None = None
170
+ if journal is not None:
171
+ journal_ref = f"journal_entry:{journal.entry_id}"
172
+ journal_state_at = journal.published_at
173
+ journal_marker = _direction_marker(journal.body.model_dump())
174
+
175
+ status_ref, status_state_at = await _resolve_status_update(
176
+ scope_type=scope_type, scope_key=scope_key, as_of=as_of, workspace_id=workspace_id
177
+ )
178
+ handoff_ref, handoff_state_at = await _resolve_handoff(
179
+ scope_type=scope_type, scope_key=scope_key, as_of=as_of, workspace_id=workspace_id
180
+ )
181
+
182
+ chain: list[tuple[DirectionSource, str | None, datetime | None]] = [
183
+ ("doc", direction_ref, direction_state_at),
184
+ ("journal", journal_ref, journal_state_at),
185
+ ("project_status", status_ref, status_state_at),
186
+ ("handoff", handoff_ref, handoff_state_at),
187
+ ]
188
+ primary: tuple[DirectionSource, str | None, datetime | None] | None = None
189
+ secondary: list[tuple[DirectionSource, str | None, datetime | None]] = []
190
+ for entry in chain:
191
+ if entry[1]:
192
+ if primary is None:
193
+ primary = entry
194
+ else:
195
+ secondary.append(entry)
196
+ if primary is None:
197
+ return BaselineReference(source="none", ref=None, confidence=0.7)
198
+
199
+ secondary_refs = [s[1] for s in secondary if s[1]]
200
+ # Conflict: at least two sources present and direction markers disagree.
201
+ # Without direction_marker on status/handoff substrate we only flag conflict
202
+ # if journal_marker exists AND a secondary source disagrees explicitly.
203
+ conflict = False
204
+ if journal_marker and secondary_refs:
205
+ # Best-effort: load secondary direction markers from handoff_summary etc.
206
+ # In v1 we mark conflict if multiple secondaries exist (proxy for
207
+ # divergence pressure). Future v1.1 will compare typed direction tags.
208
+ conflict = len(secondary_refs) >= 2
209
+
210
+ source: DirectionSource = primary[0]
211
+ confidence_base = {
212
+ "doc": 1.0, # project_directions = founder-declared
213
+ "journal": 1.0,
214
+ "project_status": 0.9,
215
+ "handoff": 0.8,
216
+ }.get(source, 0.5)
217
+ if conflict:
218
+ confidence_base = max(0.3, confidence_base - 0.1)
219
+
220
+ return BaselineReference(
221
+ source=source,
222
+ ref=primary[1],
223
+ state_at=primary[2],
224
+ confidence=confidence_base,
225
+ conflict=conflict,
226
+ secondary_refs=secondary_refs,
227
+ )
228
+
229
+
230
+ __all__ = ["resolve_baseline"]
@@ -0,0 +1,75 @@
1
+ # Brain v1 — Capabilities (sub-05 §3.1, OD-11).
2
+ # Schema metadata for agent cold-start. Mirrors graph_capabilities precedent.
3
+ # Returns the Literal members enforced at the Pydantic boundary so agents can
4
+ # discover enums without hardcoding constants.
5
+ from __future__ import annotations
6
+
7
+ from typing import get_args
8
+
9
+ from core.api.models.brain import (
10
+ BrainCapabilities,
11
+ ConfidenceTier,
12
+ DriftAxis,
13
+ EventType,
14
+ FindingApprovalState,
15
+ FindingType,
16
+ KnowledgeForm,
17
+ RunStatus,
18
+ RunTrigger,
19
+ ScopeType,
20
+ Severity,
21
+ SignalState,
22
+ SignalType,
23
+ SourceSystem,
24
+ SuggestedArtifact,
25
+ )
26
+
27
+ # Note: ApprovalState, OperationType, ClosureConditionKind are imported lazily
28
+ # to keep this module a leaf (no cycle with models.brain).
29
+
30
+ KNOWLEDGE_GLYPHS: dict[str, str] = {
31
+ "adr": "▣",
32
+ "spec": "▦",
33
+ "playbook": "⌘",
34
+ "tribal_memory": "✶",
35
+ "external_update": "↘",
36
+ "claimed_decision": "!",
37
+ "unknown": "?",
38
+ }
39
+
40
+
41
+ def get_capabilities() -> BrainCapabilities:
42
+ """Snapshot of every Literal enum exposed by the Brain v1 surface."""
43
+ from core.api.models.brain import (
44
+ ApprovalState,
45
+ ClosureConditionKind,
46
+ OperationType,
47
+ )
48
+
49
+ def _literal_members(tp: object) -> list[str]:
50
+ return [str(m) for m in get_args(tp)]
51
+
52
+ return BrainCapabilities(
53
+ schema_version=1,
54
+ event_types=_literal_members(EventType),
55
+ source_systems=_literal_members(SourceSystem),
56
+ signal_types=_literal_members(SignalType),
57
+ knowledge_forms=_literal_members(KnowledgeForm),
58
+ operation_types=_literal_members(OperationType),
59
+ finding_types=_literal_members(FindingType),
60
+ severities=_literal_members(Severity),
61
+ confidence_tiers=_literal_members(ConfidenceTier),
62
+ drift_axes=_literal_members(DriftAxis),
63
+ approval_states=_literal_members(ApprovalState),
64
+ finding_approval_states=_literal_members(FindingApprovalState),
65
+ signal_states=_literal_members(SignalState),
66
+ run_statuses=_literal_members(RunStatus),
67
+ run_triggers=_literal_members(RunTrigger),
68
+ scope_types=_literal_members(ScopeType),
69
+ suggested_artifacts=_literal_members(SuggestedArtifact),
70
+ closure_condition_kinds=_literal_members(ClosureConditionKind),
71
+ knowledge_glyphs=dict(KNOWLEDGE_GLYPHS),
72
+ )
73
+
74
+
75
+ __all__ = ["KNOWLEDGE_GLYPHS", "get_capabilities"]