---
name: server-storage
description: "@stello-ai/server 的 PG 持久化层设计决策和实现模式。触发条件：修改或引用 server 存储层。"
---

# Server Storage Layer — PG 持久化

> 相关 skill：**storage-design**（接口定义）、**server-design**（传输层）

---

## Schema 设计决策

7 张表：users, spaces, sessions, records, session_data, session_refs, core_data

- **不需要 topology_nodes 表** — TopologyNode 从 sessions 表派生（`SELECT id, parent_id, label`）
- **children/refs 不存列** — `WHERE parent_id=` 派生 children，JOIN session_refs 派生 refs
- **session_data 统一槽位** — 不同 key 存不同语义（system_prompt / insight / memory / scope / index），避免列爆炸
- **records 表共享** — session 包和 core 包各自投影字段（Message 无 metadata，TurnRecord 有）
- **CASCADE 删除** — sessions ON DELETE CASCADE 级联到 records、session_data、session_refs

---

## 3 个 Storage Adapter

| Adapter | 实现接口 | 职责 |
|---------|---------|------|
| PgSessionStorage | SessionStorage（@stello-ai/session） | 单个 Session 数据操作（含 root，所有 Session 同构） |
| PgSessionTree | SessionTree（@stello-ai/core） | 拓扑：createSession / listRoots / 树操作 / 固化配置 |
| PgMemoryEngine | MemoryEngine（@stello-ai/core） | 核心数据读写、递归上下文组装 |

批量 digest 收集由 `StelloAgent.listSessionDigests()` 在应用层组合 `SessionTree.listAll()` + `SessionStorage.getMemory/getInsight` 完成，存储层不需要提供专用方法。

---

## 关键实现模式

### spaceId 隔离
所有 adapter 构造时绑定 spaceId，所有 SQL 查询 WHERE space_id = $1。多租户隔离在查询层保证。

### Slot 统一存储
`session_data (session_id, key)` 表，UPSERT 模式 `ON CONFLICT (session_id, key) DO UPDATE`。

### 两种 SessionMeta 投影
PG 存 sessions 表超集。不同 adapter 投影为不同类型：
- PgSessionStorage → @stello-ai/session 的 SessionMeta（`id / label / status / createdAt / updatedAt`）
- PgSessionTree → @stello-ai/core 的 SessionMeta（含 scope / turnCount / lastActiveAt）+ TopologyNode（纯树结构）

### 递归 CTE
`getAncestors`、`getAllDescendantIds`、`assembleContext` 都用 `WITH RECURSIVE` 遍历树结构。

### 事务支持
`PgSessionStorage.transaction()` 通过类型判断区分 Pool 与 PoolClient。Pool 时获取独占 client 开事务，PoolClient 时直接执行。
