pe_core/scope.rs
1//! ExecutionScope — multi-tenant isolation.
2//!
3//! Every operation carries a scope. No operation happens without one.
4//! Based on Group 27 of the pre-plan.
5
6use serde::{Deserialize, Serialize};
7
8pub type TenantId = String;
9pub type UserId = String;
10pub type SessionId = String;
11pub type ThreadId = String;
12pub type TaskId = String;
13
14/// Execution scope — flows through every storage operation.
15///
16/// Isolation rules:
17/// - tenant_id: complete isolation. Tenant A never sees Tenant B.
18/// - user_id: within tenant, users share collective but not private memory.
19/// - session_id: within user, sessions share long-term memory but not in-progress state.
20/// - thread_id: within session, threads are independent conversations.
21/// - task_id: within thread, tasks track specific work items.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ExecutionScope {
24 pub tenant_id: TenantId,
25 pub user_id: UserId,
26 pub session_id: SessionId,
27 pub thread_id: ThreadId,
28 pub task_id: Option<TaskId>,
29}
30
31impl ExecutionScope {
32 pub fn new(
33 tenant_id: impl Into<String>,
34 user_id: impl Into<String>,
35 session_id: impl Into<String>,
36 thread_id: impl Into<String>,
37 ) -> Self {
38 Self {
39 tenant_id: tenant_id.into(),
40 user_id: user_id.into(),
41 session_id: session_id.into(),
42 thread_id: thread_id.into(),
43 task_id: None,
44 }
45 }
46
47 #[must_use = "builder methods return the modified builder"]
48 pub fn with_task(mut self, task_id: impl Into<String>) -> Self {
49 self.task_id = Some(task_id.into());
50 self
51 }
52
53 /// SurrealDB namespace for this scope — tenant-level isolation.
54 ///
55 /// Maps to `db.use_ns(scope.namespace())`. Each tenant gets a separate
56 /// SurrealDB namespace — complete isolation at the storage layer.
57 /// No query can cross namespace boundaries.
58 ///
59 /// Used by `SurrealCheckpointer` at construction time and
60 /// by `SurrealMemoryStore` per-call via `scoped()`.
61 pub fn namespace(&self) -> &str {
62 &self.tenant_id
63 }
64
65 /// Database name within the namespace.
66 ///
67 /// Maps to `db.use_db(scope.database())`. Fixed per deployment —
68 /// all agents share one database within a tenant namespace.
69 /// Table-level scoping (thread_id, user_id, session_id) provides
70 /// finer isolation within the database.
71 pub fn database(&self) -> &str {
72 "potential_expectations"
73 }
74}