Skip to main content

CHANGELOG.md

# Changelog

## [0.1.2] - 2026-05-25

> **Patch release** — 对齐 cmdc 0.6.0 `CMDC.Memory` behaviour 升级,
> 加入 `tags :: [String.t()]` 一等公民字段 + `tags_any` / `tags_all` 高效
> 检索操作符 + `schema_version/0` callback。**零 breaking,无需停机**。

### Added

- **`tags` 一等公民字段** — `cmdc_episodic_memories` 表新增 `tags :: text[]` 列
  (NOT NULL default `{}`),与 cmdc 主库 v0.6+ `CMDC.Memory.entry()` 标准对齐
- **`tags_any` / `tags_all` 检索操作符** — `search/3` 的 `filters` 接受:
  - `{:tags_any, ["a", "b"]}` — entry.tags 与列表有任一交集(走 PG `&&`)
  - `{:tags_all, ["a", "b"]}` — entry.tags 全部包含列表元素(走 PG `@>`)
  - 空列表 `{:tags_any, []}` / `{:tags_all, []}` 视为跳过过滤
- **GIN 索引** — `cmdc_episodic_memories(tags)` 上的 GIN 索引让 tag 检索高效
- **`c:schema_version/0` callback** — 实现 cmdc 0.6+ Memory behaviour 可选 callback,
  返回 `1`(`tags` 字段 default `{}`,老数据零 ETL,不算 schema breaking)
- **`store/3` 自动写入 tags** — `data[:tags]` / `data["tags"]` 自动落到 tags 列;
  支持 `String.t() | [String.t()] | [atom()]` 多种入参形态,normalize 为 `[string]`
- **`row_to_entry` 同步带回 tags** — 检索结果含 `:tags` 字段(老 entry 自动为 `[]`)

### Migration(0.1.1 → 0.1.2)

无破坏性变化,直接升 hex 版本号即可。运行新增 migration:

    $ mix ecto.migrate

migration 文件 `priv/repo/migrations/20260525_add_tags_to_cmdc_episodic_memories.exs`
自动:
- 在 `cmdc_episodic_memories` 加 `tags text[] NOT NULL DEFAULT '{}'` 列
- 创建 GIN 索引 `cmdc_episodic_memories_tags_index`

老数据全部自动获得 `tags = '{}'`,行为与"未传 tags 过滤"一致。
不需要业务代码改动,新代码可逐步引入 tags 检索:

    # 写入时按需要带 tags
    backend.store(nil, "ep-1",
      content: "...", user_id: "u-1", tags: ["domain:finance", "topic:tax"])

    # 检索时按 tags 过滤
    backend.search(nil, "tax",
      filters: [user_id: "u-1", {:tags_any, ["topic:tax"]}])

### Compatibility

- 老代码零改动 — `store/3` 不传 `:tags` 时落 `[]`;`search/3` 不传 `tags_*`
  filter 时返回行为不变
- `row_to_entry` 一定带 `:tags` 字段(空 entry 也是 `[]`)。如下游业务代码用
  `Map.fetch!(entry, :tags)` 会触发(老 entry migration 后也有 `[]`);若有
  下游代码 `key in Map.keys(entry)` 类逻辑会感知新键

## [0.1.1] - 2026-05-25

> **Patch release** — 多租户隔离安全修复 + 集成方反馈采纳。
> **行为变化提示**:默认行为从"未传 user_id 时返全表"修复为"返空列表"。

### Fixed — 多租户数据隔离(安全)

- **`EpisodicMemoryBackend.search/3` / `similarity_search/3`** — 未传
  `filters[:user_id]` 时的行为修复:
  - **v0.1.0 缺陷**:代码 `maybe_filter_user(query, nil), do: query` 不过滤,
    返回**全表**所有租户的 episodic 记录(注释声称"避免误泄"但实际未做)
  - **v0.1.1 修复**:新增 `:on_missing_tenant` opt 控制行为
    - `:return_empty`(**默认**)— 返回 `{:ok, []}` 强制空结果
    - `:return_error` — 返回 `{:error, :missing_tenant_filter}` 严格模式

### Added

- **`:on_missing_tenant` opt** 在 `search/3` / `similarity_search/3` 的 opts 中接受

### Migration(0.1.0 → 0.1.1)

**强烈建议多租户集成方采取以下步骤**:

1. 代码 review:确保所有调用都显式传 `filters[:user_id]`
2. 升级到 0.1.1 后短期可保持默认 `:return_empty` 行为(返空兼容,不报错)
3. 验证业务流程后,逐步迁移到 `opts[:on_missing_tenant] = :return_error`,
   让漏传 user_id 的代码路径在测试期就暴露

**与 cmdc 主库的关系**:cmdc 0.5.4 同步在 `Plugin.Builtin.EpisodicMemory`
新增 `:require_user_id: true` opt 形成"Plugin 层 + Backend 层"双重防护;
配合使用最佳实践:

    {EpisodicMemory, memory_module: CMDCMemoryPg.EpisodicMemoryBackend,
     memory_store: nil, require_user_id: true}    # Plugin 层
    # backend 调用时透传 on_missing_tenant: :return_error                # Backend 层

### Compatibility

- 默认行为变化:返"全表"→ 返"空列表";单租户场景(只有 1 个 user_id)无影响
- 多租户场景:这是**修复**,先前行为是安全 bug,升级后应严格 review 代码

## [0.1.0] - 2026-05-18

首发版本 — 与 `cmdc` 主库 0.5.0 协同发布。

### Added

- **`CMDCMemoryPg.Repo`** — Ecto Repo for cmdc_memory_pg
- **`CMDCMemoryPg.CheckpointBackend`** — 实现 `CMDC.Checkpoint.Backend` 4 callback
  - snapshot 序列化走 `:erlang.term_to_binary(snap, [:compressed])` 写入 bytea
  - 复用 cmdc 主库 `CheckpointBackend.ETS` 同套测试 suite
- **`CMDCMemoryPg.EpisodicMemoryBackend`** — 实现 `CMDC.Memory` 5 callback
  - 与 cmdc 主库 `Plugin.Builtin.EpisodicMemory` 直接对接
  - 按 `user_id` namespace 隔离多租户
  - v0.1 `similarity_search/3` 降级为 ILIKE 文本匹配(pgvector 留 v0.2)
- Ecto migration 2 张表:`cmdc_checkpoints` + `cmdc_episodic_memories`
- `docker-compose.yml` — Postgres 16 alpine 测试用
- 完整 README + Cloak encryption 集成示例

### v0.1 范围说明(明确不含)

- ❌ pgvector 真语义检索(v0.2)
- ❌ 3-tier Memory(Working / Semantic / Procedural)— 留 v0.2
- ❌ Composite 路由 backend(cmdc 主库 `Backend.Composite`)
- ❌ KV jsonb backend(v0.2)
- ❌ Cloak encryption 强制集成(提供 `Snapshot.redact/2` hook 给集成方)

### Migration

新引入,无 migration。配置 + `mix ecto.migrate` 即可使用。