post_cortex_core/services.rs
1// Copyright (c) 2025, 2026 Julius ML
2// Licensed under the MIT License. See LICENSE at the workspace root.
3
4//! Canonical service trait for post-cortex.
5//!
6//! `PostCortexService` is the **single internal entrypoint** that every
7//! transport layer (gRPC, MCP, REST, future SDKs) delegates to. Per
8//! TODO.md:106-117 we never want two transports re-implementing the same
9//! operation with subtly different validation; they each translate their
10//! wire payload, then call the same method on this trait. The canonical
11//! impl lives in [`post-cortex-memory`](https://docs.rs/post-cortex-memory)
12//! as `MemoryServiceImpl`.
13//!
14//! ## Scope
15//!
16//! Phase 4 introduces the trait skeleton with the **read/write/search/manage**
17//! operations every transport exposes. Phases 6 + 7 (MCP and daemon
18//! extraction) migrate the existing handlers to delegate here; until then
19//! both transports still call into `ConversationMemorySystem` directly.
20//!
21//! Trait methods intentionally take small, immediately-usable request
22//! types defined in `types` rather than huge proto structs — keeping
23//! the surface readable from non-gRPC consumers. gRPC handlers do the
24//! proto-to-domain translation in a single `From`/`Into` module at the
25//! transport boundary (Phase 7).
26//!
27//! ## Object safety
28//!
29//! The trait is intentionally object-safe (no generic methods, no `Self`
30//! return types) so consumers — `post-cortex-mcp` in particular — can
31//! hold an `Arc<dyn PostCortexService>` and let downstream Rust projects
32//! plug in their own implementation without touching the MCP layer.
33
34use std::sync::Arc;
35
36use async_trait::async_trait;
37
38use crate::core::error::SystemError;
39
40pub mod types;
41
42pub use types::*;
43
44/// Canonical post-cortex service.
45///
46/// Every transport (gRPC, MCP, REST, future SDKs) delegates to this trait
47/// so there is exactly one implementation per operation across the
48/// codebase. See the module-level docs for the design rationale.
49///
50/// Default implementations are kept minimal — the production impl is
51/// `post_cortex_memory::services::MemoryServiceImpl`. Downstream Rust
52/// projects can implement this trait against their own storage +
53/// embeddings stack and reuse the rest of the post-cortex ecosystem
54/// (MCP tools, gRPC client, summary view).
55#[async_trait]
56pub trait PostCortexService: Send + Sync + 'static {
57 // ------------------------------------------------------------------
58 // Liveness
59 // ------------------------------------------------------------------
60
61 /// Returns a snapshot of system health. Cheap — the only operation
62 /// that does **not** flow through the storage actor and is safe to
63 /// call from a heartbeat probe.
64 async fn health(&self) -> Result<HealthReport, SystemError>;
65
66 // ------------------------------------------------------------------
67 // Write path
68 // ------------------------------------------------------------------
69
70 /// Persist a single context update for a session and enqueue derived
71 /// work (embedding, HNSW upsert, graph update, summary refresh)
72 /// onto background pipelines. Returns once the entry is durably
73 /// persisted; embeddings / vector index land asynchronously per
74 /// TODO.md:136-145.
75 async fn update_context(
76 &self,
77 req: UpdateContextRequest,
78 ) -> Result<UpdateContextResponse, SystemError>;
79
80 /// Batch variant of [`Self::update_context`]. Backends use storage
81 /// write batches when available (RocksDB transaction).
82 async fn bulk_update_context(
83 &self,
84 req: BulkUpdateContextRequest,
85 ) -> Result<BulkUpdateContextResponse, SystemError>;
86
87 // ------------------------------------------------------------------
88 // Read path
89 // ------------------------------------------------------------------
90
91 /// Run a semantic similarity search against persisted content. Scope
92 /// determines whether the query runs against a single session, a
93 /// workspace, or the global index.
94 async fn semantic_search(
95 &self,
96 req: SemanticSearchRequest,
97 ) -> Result<SemanticSearchResponse, SystemError>;
98
99 /// Run a structured/keyword query against the session's context
100 /// updates. Faster than semantic search; no embedding required.
101 async fn query_context(
102 &self,
103 req: QueryContextRequest,
104 ) -> Result<QueryContextResponse, SystemError>;
105
106 /// Graph-aware retrieval: semantic search + entity neighbourhood
107 /// traversal + impact analysis, all merged into one assembled
108 /// context payload.
109 async fn assemble_context(
110 &self,
111 req: AssembleContextRequest,
112 ) -> Result<AssembleContextResponse, SystemError>;
113
114 // ------------------------------------------------------------------
115 // Session management
116 // ------------------------------------------------------------------
117
118 /// Manage session lifecycle (create / list / load / search / update /
119 /// delete). The variant carried in the request determines which
120 /// operation runs — letting callers (especially MCP) treat sessions
121 /// as a single tool surface.
122 async fn manage_session(
123 &self,
124 req: ManageSessionRequest,
125 ) -> Result<ManageSessionResponse, SystemError>;
126
127 // ------------------------------------------------------------------
128 // Workspace management
129 // ------------------------------------------------------------------
130
131 /// Manage workspace lifecycle (create / list / get / delete /
132 /// add-session / remove-session). Same shape as
133 /// [`Self::manage_session`].
134 async fn manage_workspace(
135 &self,
136 req: ManageWorkspaceRequest,
137 ) -> Result<ManageWorkspaceResponse, SystemError>;
138
139 // ------------------------------------------------------------------
140 // Entity maintenance
141 // ------------------------------------------------------------------
142
143 /// Manage entity lifecycle (delete / delete-update). Cascades through
144 /// the entity graph; storage-layer side effects (vector index purge,
145 /// summary invalidation) run on background pipelines.
146 async fn manage_entity(
147 &self,
148 req: ManageEntityRequest,
149 ) -> Result<ManageEntityResponse, SystemError>;
150
151 // ------------------------------------------------------------------
152 // Analytics & summaries
153 // ------------------------------------------------------------------
154
155 /// Compose the structured summary view for a session — a
156 /// hierarchical projection over context updates, decisions,
157 /// problems, and entity importance.
158 async fn get_structured_summary(
159 &self,
160 req: StructuredSummaryRequest,
161 ) -> Result<StructuredSummaryResponse, SystemError>;
162
163 // ------------------------------------------------------------------
164 // Admin
165 // ------------------------------------------------------------------
166
167 /// Run an admin operation (vectorize session, create checkpoint,
168 /// fetch vectorization stats, etc.). Variant-dispatched like
169 /// session/workspace/entity for symmetry.
170 async fn admin(&self, req: AdminRequest) -> Result<AdminResponse, SystemError>;
171}
172
173/// Convenience alias — most consumers carry the trait behind an `Arc`.
174pub type DynPostCortexService = Arc<dyn PostCortexService>;