claude-mem-lite 5.5.1 → 6.0.0

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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "5.5.1",
13
+ "version": "6.0.0",
14
14
  "source": "./",
15
15
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "5.5.1",
3
+ "version": "6.0.0",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  # claude-mem-lite
4
4
 
5
- `claude-mem-lite` is a **persistent memory** (also called *long-term memory* or *cross-session context*) system for **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** — Anthropic's CLI coding agent. It runs as an **[MCP](https://modelcontextprotocol.io/) server** plus a set of Claude Code hooks, automatically capturing coding observations, decisions, and bug fixes during sessions, then providing hybrid full-text + semantic search to recall them later.
5
+ `claude-mem-lite` is a **persistent memory** (also called *long-term memory* or *cross-session context*) system for **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** — Anthropic's CLI coding agent. It runs as an **[MCP](https://modelcontextprotocol.io/) server** plus a set of Claude Code hooks, automatically capturing coding observations, decisions, and bug fixes during sessions, then providing full-text search with query expansion to recall them later.
6
6
 
7
- Compared to general-purpose LLM memory frameworks like [`mem0`](https://github.com/mem0ai/mem0) or the MCP reference [`memory`](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) server, claude-mem-lite is purpose-built for Claude Code's hook lifecycle: episode batching cuts LLM calls 7–10× vs the original [claude-mem](https://github.com/thedotmack/claude-mem) (an estimated ~600× lower total cost — see the cost model below; this is an architecture estimate, not a measured benchmark), while the hybrid FTS5 + TF-IDF retriever benchmarks at 0.90 Recall@10 / 0.85 Precision@10
7
+ Compared to general-purpose LLM memory frameworks like [`mem0`](https://github.com/mem0ai/mem0) or the MCP reference [`memory`](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) server, claude-mem-lite is purpose-built for Claude Code's hook lifecycle: episode batching cuts LLM calls 7–10× vs the original [claude-mem](https://github.com/thedotmack/claude-mem) (an estimated ~600× lower total cost — see the cost model below; this is an architecture estimate, not a measured benchmark), while the FTS5 retriever benchmarks at 0.90 Recall@10 / 0.85 Precision@10
8
8
  (see [Search Quality](#search-quality) for the reproduction command).
9
9
 
10
- > 中文简介:claude-mem-lite 是 Claude Code 的轻量级**持久化记忆 / 长期记忆 / 跨会话上下文**插件,基于 MCP 协议 + 钩子机制,自动捕获编码会话中的决策、修复和上下文,并通过 FTS5 + TF-IDF 混合检索召回。详见 [中文 README](README.zh-CN.md)。
10
+ > 中文简介:claude-mem-lite 是 Claude Code 的轻量级**持久化记忆 / 长期记忆 / 跨会话上下文**插件,基于 MCP 协议 + 钩子机制,自动捕获编码会话中的决策、修复和上下文,并通过 FTS5 全文检索召回。详见 [中文 README](README.zh-CN.md)。
11
11
 
12
12
  Zero external services. Single SQLite database. Minimal overhead.
13
13
 
@@ -66,7 +66,7 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
66
66
  | **Target client** | Claude Code only | Any LLM app via SDK | Any MCP client | Claude Code only |
67
67
  | **Capture model** | Auto via hooks | Manual `memory.add()` | Manual tool calls (`create_entities`, `add_observations`) | Auto via hooks |
68
68
  | **Code-aware retrieval** | FTS5 + 100+ synonym pairs (incl. CJK↔EN) | General-purpose | Generic graph nodes | Code-aware |
69
- | **Search** | Hybrid: FTS5 BM25 + TF-IDF cosine via RRF | Hybrid: semantic + BM25 + entity linking | Knowledge-graph traversal | FTS5 + Chroma vector |
69
+ | **Search** | FTS5 BM25 + query expansion (PRF, concept co-occurrence) | Hybrid: semantic + BM25 + entity linking | Knowledge-graph traversal | FTS5 + Chroma vector |
70
70
  | **Storage** | Single local SQLite | Pluggable; Qdrant or configurable vector store | Single JSONL file (knowledge graph) | SQLite + Chroma |
71
71
  | **LLM dependency** | Haiku per episode (5–10 ops batched) | LLM per add/search op | None (graph CRUD only) | Sonnet per tool call |
72
72
  | **Setup** | One command (`/plugin install` or `npx`) | SDK integration + vector store config | MCP install (per-client) | Bun + Python + Chroma |
@@ -76,7 +76,7 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
76
76
  ## Features
77
77
 
78
78
  - **Automatic capture** -- Hooks into the Claude Code lifecycle (SessionStart, PreCompact, PreToolUse, PostToolUse, PostToolUseFailure, Stop, UserPromptSubmit — the seven events in `hooks/hooks.json`) to record observations without manual effort
79
- - **Hybrid search** -- FTS5 BM25 + TF-IDF vector cosine similarity, merged via Reciprocal Rank Fusion (RRF). FTS5 handles keyword matching; 512-dim TF-IDF vectors capture semantic similarity for recall beyond exact terms
79
+ - **Lexical search with query expansion** -- FTS5 BM25 scoring, an AND->OR rescue pass, pseudo-relevance feedback and concept co-occurrence. A TF-IDF vector arm shipped alongside it until it was measured net-negative and removed; `--deep` still fuses multiple LLM-rewritten queries with Reciprocal Rank Fusion
80
80
  - **Timeline browsing** -- Navigate observations chronologically with anchor-based context windows
81
81
  - **Episode batching** -- Groups related file operations into coherent episodes before LLM encoding
82
82
  - **Error-triggered recall** -- Automatically searches memory when Bash errors occur, surfacing relevant past fixes
@@ -92,8 +92,7 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
92
92
  - **Two-tier dedup** -- Jaccard similarity (5-minute window) + MinHash signatures (7-day cross-session window) prevent duplicates
93
93
  - **Synonym expansion** -- Abbreviations like `K8s`, `DB`, `auth` automatically expand to full forms in FTS5 search (100+ pairs including CJK↔EN cross-language mappings)
94
94
  - **CJK synonym extraction** -- Unsegmented Chinese text is scanned for known vocabulary words (数据库→database, 搜索→search, etc.) enabling cross-language memory recall
95
- - **Stop-word filtering** -- English stop words filtered from both TF-IDF vocabulary (reclaiming ~18% of vector dimensions) and FTS queries (preventing false negatives from noise terms like "how", "the", "does")
96
- - **Persisted vocabulary** -- TF-IDF vocabulary persisted to `vocab_state` table, preventing vector staleness when document frequencies shift. Vectors stay valid until explicit rebuild
95
+ - **Stop-word filtering** -- English stop words filtered from FTS queries, preventing false negatives from noise terms like "how", "the", "does"
97
96
  - **Pseudo-relevance feedback (PRF)** -- Top results seed expansion queries for broader recall
98
97
  - **Concept co-occurrence** -- Shared concepts across observations expand search to related topics
99
98
  - **Context-aware re-ranking** -- Active file overlap boosts relevance (exact match + directory-level half-weight)
@@ -124,10 +123,10 @@ How claude-mem-lite differs from the major neighbors in the LLM-memory space (ve
124
123
  - **Git-SHA continuation anchor** (v2.31.0) -- Handoff rows include `git_sha_at_handoff`; any handoff matching the current `HEAD` counts as continuation regardless of TTL. Code state is a stronger continuation signal than wall-clock time
125
124
  - **Startup dashboard** (v2.31.0) -- SessionStart hook aggregates `git status` + `~/.claude/tasks/*.json` + `~/.claude/plans/*.md` + most-recent exit handoff + recent event count into a single structured block injected via `hookSpecificOutput.additionalContext`
126
125
  - **Activity namespace** (v2.31.0) -- Dedicated `events` table + FTS5 for non-memdir types (`bugfix`, `lesson`, `bug`, `discovery`, `refactor`, `feature`, `observation`, `decision`) that don't compete with `WHAT_NOT_TO_SAVE` semantics on the observations table. CLI: `claude-mem-lite activity save|search|recent|show`. `hook-llm` routes non-memdir summary types through `persistHaikuSummary` so upgrades from observations→events are atomic. (v3.39: the `/lesson` and `/bug` slash commands were redirected from this events table to searchable **observations** — `mem_search` never read the events table, so explicit saves were unfindable; the events table remains the auto-capture activity log.)
127
- - **In-place observation updates** -- `mem_update` tool modifies existing observations atomically (field update + FTS text rebuild + vector re-computation in one transaction), preserving original IDs and references
126
+ - **In-place observation updates** -- `mem_update` tool modifies existing observations atomically (field update + FTS text rebuild in one transaction), preserving original IDs and references
128
127
  - **Bulk export** -- `mem_export` tool exports observations as JSON or JSONL, with project/type/date filtering and 1000-row pagination cap with batch guidance
129
128
  - **FTS integrity management** -- `mem_fts_check` tool verifies FTS5 index health or rebuilds indexes on demand, useful after database recovery or when search results seem wrong
130
- - **Atomic multi-table writes** -- `saveObservation` wraps observations + observation_files + observation_vectors INSERTs in a single `db.transaction()`, preventing orphaned rows on crash
129
+ - **Atomic multi-table writes** -- `saveObservation` wraps the observations + observation_files INSERTs in a single `db.transaction()`, preventing orphaned rows on crash
131
130
  - **Modular NLP pipeline** -- Synonym maps, stop words, scoring constants, and query building extracted into focused modules (`synonyms.mjs`, `stop-words.mjs`, `scoring-sql.mjs`, `nlp.mjs`) for independent testing and maintenance
132
131
  - **Porter-aligned PRF** -- Pseudo-relevance feedback terms are now stemmed with the same Porter algorithm used by FTS5, ensuring PRF expansion terms match the search index
133
132
 
@@ -229,6 +228,34 @@ rm -rf ~/claude-mem-lite/ # pre-v0.5 unhidden (if not auto-moved)
229
228
  repos/ # Shallow-cloned source repos
230
229
  ```
231
230
 
231
+ <!-- vector-arm-removal-note:start -->
232
+ ## Upgrading to 6.0.0 (breaking)
233
+
234
+ **The default search path does not change.** 6.0.0 removes the TF-IDF vector arm, which has
235
+ been disabled by default since 3.17.0 — if you never set CLAUDE_MEM_VECTORS, upgrading is
236
+ behaviour-identical and there is nothing to do.
237
+
238
+ Three surfaces are gone:
239
+
240
+ | Removed | What happens now |
241
+ |---|---|
242
+ | `CLAUDE_MEM_VECTORS=1` | Inert. Setting it has no effect. |
243
+ | `maintain execute --ops rebuild_vectors` | Exits 1: `Unknown operation(s): rebuild_vectors`. |
244
+ | Tables `observation_vectors`, `vocab_state` | Dropped by schema migration v49 on first open. |
245
+
246
+ **The migration is one-way.** Once a 6.0.0 build has opened your database, older versions
247
+ refuse it — `schema.mjs`'s forward-incompat guard throws *"DB schema is v49 but this
248
+ claude-mem-lite binary supports up to v48"*. If you want to stay on the vector arm, pin
249
+ `claude-mem-lite@5.6.0` **before** upgrading. If you have already upgraded and need to go
250
+ back, either re-upgrade, point `CLAUDE_MEM_DIR` at a fresh directory, or restore a
251
+ pre-upgrade backup (`claude-mem-lite export` / the snapshots under your data dir).
252
+
253
+ Why it was removed: measured directly against the shipped path, the arm was negative on both
254
+ benchmark fixtures — including the vocabulary-mismatch suite that is the only reason a vector
255
+ arm would exist (Recall@10 0.3407 → 0.3018, and roughly +88% P95 latency). No observations
256
+ are lost; only the derived vector index is.
257
+ <!-- vector-arm-removal-note:end -->
258
+
232
259
  ## Usage
233
260
 
234
261
  ### MCP Tools (used automatically by Claude)
@@ -265,7 +292,7 @@ surface — reach them through the CLI column in the second table.
265
292
  | `mem_stats` | `claude-mem-lite stats` | Counts, type distribution, daily activity. |
266
293
  | `mem_delete` | `claude-mem-lite delete <id>` | Preview / confirm workflow, FTS5 cleanup. |
267
294
  | `mem_compress` | `claude-mem-lite compress` | Roll up old low-value observations (preview default; `--execute` to apply). |
268
- | `mem_maintain` | `claude-mem-lite maintain scan --ops dedup,decay` | dedup / decay / cleanup / rebuild_vectors (`scan` previews, `execute` applies). |
295
+ | `mem_maintain` | `claude-mem-lite maintain scan --ops dedup,decay` | dedup / decay / cleanup / vacuum (`scan` previews, `execute` applies). |
269
296
  | `mem_optimize` | `claude-mem-lite optimize` | LLM-powered re-enrich / normalize / cluster-merge (preview default; `--run` to apply). |
270
297
  | `mem_export` | `claude-mem-lite export` | JSON / JSONL dump, filters by project, type, date. |
271
298
  | `mem_fts_check` | `claude-mem-lite fts-check <check\|rebuild>` | FTS5 integrity + rebuild. |
@@ -413,16 +440,6 @@ key_files, key_decisions, match_keywords, created_at_epoch
413
440
  obs_id, filename
414
441
  ```
415
442
 
416
- **observation_vectors** -- TF-IDF vector embeddings for hybrid search
417
- ```
418
- observation_id, vector (BLOB Float32Array), vocab_version, created_at_epoch
419
- ```
420
-
421
- **vocab_state** -- Persisted TF-IDF vocabulary for stable vector indexing
422
- ```
423
- term, term_index, idf, version, created_at_epoch
424
- ```
425
-
426
443
  FTS5 indexes: `observations_fts` (title, subtitle, narrative, text, facts, concepts, lesson_learned), `session_summaries_fts`, `user_prompts_fts`
427
444
 
428
445
  ## How It Works
@@ -617,7 +634,7 @@ claude-mem-lite/
617
634
  hook-semaphore.mjs # LLM concurrency control: file-based semaphore for background workers
618
635
  schema.mjs # Database schema: single source of truth for tables, migrations, FTS5
619
636
  tool-schemas.mjs # Shared Zod schemas for MCP tool validation
620
- tfidf.mjs # TF-IDF vector engine: tokenization, vocabulary building, vector computation, cosine similarity, RRF merge
637
+ tfidf.mjs # tokenization + Porter stemming (name is historical: the TF-IDF vector engine it held was removed)
621
638
  tier.mjs # Temporal tier system: activity-based time window classification
622
639
  utils.mjs # Re-export hub: backward-compatible surface for all utility modules
623
640
  nlp.mjs # FTS5 query building: synonym expansion, CJK bigrams, sanitization
@@ -650,7 +667,7 @@ claude-mem-lite/
650
667
  ## Search Quality
651
668
 
652
669
  Benchmarked on 200 observations across 30 queries (standard + hard-negative categories),
653
- measuring the **production-hybrid** retriever (FTS5 BM25 + TF-IDF vector + RRF) — the path
670
+ measuring the **production-hybrid** retriever (the real `searchObservationsHybrid`) — the path
654
671
  `mem_search` / `recall` actually use. The CI gate (`npm run benchmark:gate`) runs this same
655
672
  path and fails on regression.
656
673
 
@@ -668,12 +685,14 @@ path and fails on regression.
668
685
  > `npm run benchmark:gate` fails the build when a run drifts more than 5% from it. This is
669
686
  > the single source for every retrieval figure quoted in this README.
670
687
 
671
- > **Note on the path measured.** Earlier versions of this table reported the *lexical*
672
- > FTS-only path (Precision@10 0.96, P95 0.15ms). The hybrid vector arm trades raw
673
- > precision@10 for higher recall / nDCG / MRR by surfacing semantically-related candidates
674
- > beyond exact lexical matches; the gate now measures the hybrid path so these numbers
675
- > reflect real `mem_search` behavior. For field-comparable recall, see the LongMemEval
676
- > section below.
688
+ > **Note on the path measured.** This table measures whatever `mem_search` actually runs,
689
+ > which is why the figures have moved twice. An older revision reported a narrower FTS-only
690
+ > harness (Precision@10 0.96, P95 0.15ms); a later one attributed the lower precision to a
691
+ > TF-IDF vector arm "trading precision for recall". **That attribution was wrong and the
692
+ > claim is withdrawn** — the gate's `hybrid_over_bm25` delta never executed a vector path at
693
+ > all, so it could not have measured that trade. The arm was later A/B'd directly, came out
694
+ > negative on both fixtures, and was removed; these numbers are the shipped path with no
695
+ > vector arm in it. For field-comparable recall, see the LongMemEval section below.
677
696
 
678
697
  ### Recall on LongMemEval (standard benchmark)
679
698
 
@@ -692,7 +711,7 @@ and `benchmark/longmemeval-rerank.mjs` (rerank).
692
711
 
693
712
  | Retriever (zero embeddings) | @1 | @5 | @10 |
694
713
  |---|---|---|---|
695
- | Lexical hybrid — FTS5 + TF-IDF + RRF | **83.4%** | **95.2%** | **96.0%** |
714
+ | Lexical — FTS5 BM25 + query expansion | **83.4%** | **95.2%** | **96.0%** |
696
715
  | + one top-20 LLM rerank pass † | 92.8% | 96.8% | 97.4% |
697
716
 
698
717
  *n = 500 questions.* The lexical row was re-measured 2026-07-18: the v3.39–v3.45
@@ -804,7 +823,6 @@ benchmark and A/B harness are calibrated against — changing them invalidates t
804
823
  | `CLAUDE_MEM_DEEP_DISCLOSURE` | `off` suppresses the one-line caveat appended to a multi-variant deep result. The caveat exists because deep search fills the page even when the corpus cannot answer — measured at 10 of 10 slots on queries whose answers had been removed (`benchmark/deep-search-holdout.mjs`) — and `deep` is AUTO by default on the MCP surface, i.e. it escalates precisely when the honest answer is "nothing". It does not change retrieval, ranking, or which rows are returned. | _(on)_ |
805
824
  | `CLAUDE_MEM_REACH_DISCLOSURE` | `off` suppresses the one-line note that fires when a search's reported `total` exceeds what its pagination can hand back. The candidate pool is sized from `limit` alone and deliberately does not grow with `offset` (D#30 — an offset-scaled pool re-ranks its own prefix under RRF, so pages overlapped and gapped), while `total` is the full match count. Measured on a 128-row corpus: at the default limit of 20 the last non-empty offset is 59, so 60 of 128 rows are unreachable at any offset. The note reports that; it does not change retrieval, ranking, or which rows are returned. | _(on)_ |
806
825
  | `CLAUDE_MEM_AUTO_DEEP_CLI` | `0` disables the same auto-escalation on the CLI path only. | _(auto)_ |
807
- | `CLAUDE_MEM_VECTORS` | `1` re-enables the persisted TF-IDF vector arm (off by default; also needs a vector rebuild via `maintain`). | _(off)_ |
808
826
  | `CLAUDE_MEM_SCOPE_FILTER` | `1` stops environment-scoped observations from firing on file-triggered recall. They stay reachable via search. **Leave it off**: on the face it gates, `environment` is not the low-relevance class its premise assumes — it cites at least as well as `project` (47.5% vs 44.3%, intervals overlapping), and an earlier measurement left 173 recall groups empty with it on. | _(off)_ |
809
827
  | `CLAUDE_MEM_READS_CARRY` | An episode flush collects `reads-<project>.txt` only when it will actually save an observation, so a flush that records nothing no longer discards the Read paths it swept up (42.2% of the paths a flush consumed, measured over 1122 transcripts). `0` restores the pre-v3.83.0 behaviour. | _(on)_ |
810
828
 
package/README.zh-CN.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # claude-mem-lite
4
4
 
5
- `claude-mem-lite` 是 **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)**(Anthropic 官方 CLI 编程代理)的 **持久化记忆系统**(也称 **长期记忆 / 跨会话上下文 / Claude Code 记忆插件**)。它以 **[MCP](https://modelcontextprotocol.io/) 服务器** + Claude Code 钩子(hooks)的形式运行,在编码会话中自动捕获观察记录、决策、bug 修复,并通过 FTS5 全文检索 + TF-IDF 向量的混合检索召回历史上下文。
5
+ `claude-mem-lite` 是 **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)**(Anthropic 官方 CLI 编程代理)的 **持久化记忆系统**(也称 **长期记忆 / 跨会话上下文 / Claude Code 记忆插件**)。它以 **[MCP](https://modelcontextprotocol.io/) 服务器** + Claude Code 钩子(hooks)的形式运行,在编码会话中自动捕获观察记录、决策、bug 修复,并通过 FTS5 全文检索(BM25 + 查询扩展)召回历史上下文。
6
6
 
7
- 与 [`mem0`](https://github.com/mem0ai/mem0)、MCP 官方参考实现的 [`memory`](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) 服务器等通用 LLM 记忆框架相比,claude-mem-lite 专为 Claude Code 的钩子生命周期定制:episode 批处理把 LLM 调用量相比原版 [claude-mem](https://github.com/thedotmack/claude-mem) 减少 7-10 倍(综合成本估算下降约 600 倍 —— 见下方成本模型,属架构估算而非实测基准);FTS5 + TF-IDF 混合检索在 30 个查询的基准上达到 **Recall@10 = 0.90 / Precision@10 = 0.85**(复现命令见[搜索质量](#搜索质量)一节)。
7
+ 与 [`mem0`](https://github.com/mem0ai/mem0)、MCP 官方参考实现的 [`memory`](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) 服务器等通用 LLM 记忆框架相比,claude-mem-lite 专为 Claude Code 的钩子生命周期定制:episode 批处理把 LLM 调用量相比原版 [claude-mem](https://github.com/thedotmack/claude-mem) 减少 7-10 倍(综合成本估算下降约 600 倍 —— 见下方成本模型,属架构估算而非实测基准);FTS5 检索在 30 个查询的基准上达到 **Recall@10 = 0.90 / Precision@10 = 0.85**(复现命令见[搜索质量](#搜索质量)一节)。
8
8
 
9
9
  无需外部服务。单一 SQLite 数据库。开销极低。
10
10
 
@@ -192,6 +192,30 @@ rm -rf ~/claude-mem-lite/ # v0.5 前的非隐藏目录(如未自动迁移)
192
192
  repos/ # 浅克隆的源代码仓库
193
193
  ```
194
194
 
195
+ <!-- vector-arm-removal-note:start -->
196
+ ## 升级到 6.0.0(破坏性变更)
197
+
198
+ **默认检索路径不变。** 6.0.0 移除了 TF-IDF 向量臂——它自 3.17.0 起就默认关闭,如果你从未设置过
199
+ CLAUDE_MEM_VECTORS,升级前后行为完全一致,无需任何操作。
200
+
201
+ 三个面被移除:
202
+
203
+ | 移除项 | 现在的行为 |
204
+ |---|---|
205
+ | `CLAUDE_MEM_VECTORS=1` | 失效。设置它不再有任何作用。 |
206
+ | `maintain execute --ops rebuild_vectors` | 退出码 1:`Unknown operation(s): rebuild_vectors`。 |
207
+ | `observation_vectors`、`vocab_state` 两张表 | 由 schema 迁移 v49 在首次打开时 DROP。 |
208
+
209
+ **这个迁移是单向的。** 一旦 6.0.0 打开过你的数据库,旧版本就会拒绝它——`schema.mjs` 的
210
+ forward-incompat 守卫会抛出 *"DB schema is v49 but this claude-mem-lite binary supports up to
211
+ v48"*。想继续用向量臂,请在**升级之前**锁定 `claude-mem-lite@5.6.0`;如果已经升级又需要回退,
212
+ 只能重新升级、把 `CLAUDE_MEM_DIR` 指向一个新目录,或从升级前的备份恢复
213
+ (`claude-mem-lite export` 或数据目录下的快照)。
214
+
215
+ 移除原因:直接对着出货路径实测,该臂在两个基准语料上都是负的——包括「词表不匹配」这个向量臂唯一
216
+ 的存在理由(Recall@10 0.3407 → 0.3018,P95 延迟约 +88%)。观察记录不会丢失,丢的只是派生的向量索引。
217
+ <!-- vector-arm-removal-note:end -->
218
+
195
219
  ## 使用方法
196
220
 
197
221
  ### MCP 工具
@@ -227,7 +251,7 @@ README 和 `docs/ARCHITECTURE.md` 都钉在它上面。)
227
251
  | `mem_stats` | `claude-mem-lite stats` | 计数、类型分布、每日活动。 |
228
252
  | `mem_delete` | `claude-mem-lite delete <id>` | 预览 / 确认流程,FTS5 自动清理。 |
229
253
  | `mem_compress` | `claude-mem-lite compress` | 压缩旧的低价值观察(默认 preview;`--execute` 执行)。 |
230
- | `mem_maintain` | `claude-mem-lite maintain scan --ops dedup,decay` | 去重 / decay / 清理 / 向量重建(`scan` 预览,`execute` 执行)。 |
254
+ | `mem_maintain` | `claude-mem-lite maintain scan --ops dedup,decay` | 去重 / decay / 清理 / vacuum(`scan` 预览,`execute` 执行)。 |
231
255
  | `mem_optimize` | `claude-mem-lite optimize` | LLM 深度优化:re-enrich / normalize / cluster-merge(默认 preview;`--run` 执行)。 |
232
256
  | `mem_export` | `claude-mem-lite export` | JSON / JSONL 导出,支持项目/类型/日期过滤。 |
233
257
  | `mem_fts_check` | `claude-mem-lite fts-check <check\|rebuild>` | FTS5 完整性检查与重建。 |
@@ -528,7 +552,7 @@ claude-mem-lite/
528
552
  ## 搜索质量
529
553
 
530
554
  基于 200 条观察和 30 个查询(标准 + 困难负样本类别)的基准测试结果,测量的是
531
- **production-hybrid** 检索路径(FTS5 BM25 + TF-IDF 向量 + RRF)——也就是 `mem_search` /
555
+ **production-hybrid** 检索路径(真实的 `searchObservationsHybrid`)——也就是 `mem_search` /
532
556
  `recall` 实际走的那条路径:
533
557
 
534
558
  | 指标 | 得分(production-hybrid) |
@@ -543,9 +567,11 @@ claude-mem-lite/
543
567
  > 固定语料、固定查询集、无采样)。CI 参考快照是 `benchmark/baseline.json`,
544
568
  > `npm run benchmark:gate` 在偏离超过 5% 时让构建失败。本 README 中所有检索指标都以此为唯一来源。
545
569
 
546
- > **关于测量路径。** 本表早期版本报告的是 *lexical* 纯 FTS 路径(Precision@10 0.96、
547
- > P95 0.15ms)。混合向量臂用 precision@10 换取更高的 recall / nDCG / MRR——它会召回超出字面
548
- > 匹配的语义相关候选;门控现在测量混合路径,所以这些数字反映的是 `mem_search` 的真实行为。
570
+ > **关于测量路径。** 本表测量的始终是 `mem_search` 实际走的那条路径,所以数字变过两次。
571
+ > 早期版本报告的是更窄的纯 FTS 测量口径(Precision@10 0.96、P95 0.15ms);后来一版把
572
+ > precision 的下降归因于「TF-IDF 向量臂用 precision 换 recall」。**那个归因是错的,该说法已撤回**
573
+ > ——门控的 `hybrid_over_bm25` 差值两个臂都不执行向量路径,根本量不到这笔交换。该臂后来被直接
574
+ > A/B 实测,两个语料上都是负的,已移除;此处的数字就是不含向量臂的出货路径。
549
575
 
550
576
  ## 开发
551
577
 
package/adopt-content.mjs CHANGED
@@ -174,7 +174,7 @@ PreToolUse hook 在你 Read / Edit / Write 文件前已自动 \`mem_recall\` 该
174
174
  | 改某条 | \`${CLI} update <id> [--lesson "<≤500>"] [--title T] [--type T] [--importance 1-3] [--narrative T] [--concepts "a b c"]\` |
175
175
  | 事件日志 | \`${CLI} activity save --type <bugfix\\|lesson\\|bug\\|discovery\\|refactor\\|feature\\|observation\\|decision> "<title>" [--body T] [--files f1,f2]\` |
176
176
 
177
- \`maintain\` / \`optimize\` / \`compress\` 见上方「维护 / 管理类工具」;\`maintain --ops\` 取值 \`cleanup,decay,boost,demote_pinned,dedup,purge_stale,rebuild_vectors,vacuum\`,省略时默认 \`cleanup,decay,boost,demote_pinned\`(顺序有意义:demote_pinned 必须在 boost 之后);\`--retain-days\` ∈ [7,365]。
177
+ \`maintain\` / \`optimize\` / \`compress\` 见上方「维护 / 管理类工具」;\`maintain --ops\` 取值 \`cleanup,decay,boost,demote_pinned,dedup,purge_stale,vacuum\`,省略时默认 \`cleanup,decay,boost,demote_pinned\`(顺序有意义:demote_pinned 必须在 boost 之后);\`--retain-days\` ∈ [7,365]。
178
178
 
179
179
  ## 卸载 / 关闭
180
180
 
package/deep-search.mjs CHANGED
@@ -33,8 +33,7 @@
33
33
 
34
34
  import { searchObservationsHybrid } from './search-engine.mjs';
35
35
  import { sanitizeFtsQuery } from './utils.mjs';
36
- import { RRF_K } from './tfidf.mjs';
37
- import { rrfAccumulate } from './lib/rrf.mjs';
36
+ import { RRF_K, rrfAccumulate } from './lib/rrf.mjs';
38
37
  import { llmRerankOrder, defaultRerankLLM } from './rerank.mjs';
39
38
  import { liveObsFilterSql } from './lib/inject-search-core.mjs';
40
39
 
@@ -425,7 +424,7 @@ export async function rewriteQuery(query, { llm = defaultLLM, retries = 1, cache
425
424
  /**
426
425
  * N-way Reciprocal Rank Fusion. Each ranked list contributes 1/(k + rank) to an
427
426
  * item's score (rank is 0-based array position; lists must already be in
428
- * relevance order). Same k=RRF_K and 1/(k+rank+1) formula as tfidf.rrfMerge,
427
+ * relevance order). k=RRF_K and the 1/(k+rank+1) formula come from lib/rrf.mjs,
429
428
  * generalized from 2 lists to N. A single list is returned in its original order
430
429
  * (scores are strictly decreasing in rank), which is what guarantees deepSearch
431
430
  * never reorders the baseline when the rewrite fails.
@@ -566,12 +565,14 @@ export async function deepSearch(
566
565
  }
567
566
  // rrfFuseN fuses by array index as rank, so each list MUST already be in
568
567
  // composite-score order. searchObservationsHybrid appends downweighted
569
- // concept(×0.7)/PRF(×0.6) expansion rows to the TAIL unsorted and, on the
570
- // vectors-disabled path, returns BEFORE the sort its vector arm applies
571
- // (search-engine.mjs:430) so a sparse variant (common in deep search, the
572
- // vocabulary-mismatch path) would hand a tail-ranked expansion row to RRF at a
573
- // worse rank than its score earns. Sort so index == composite rank, mirroring
574
- // the in-engine sort that already guards the vector-RRF merge.
568
+ // concept(×0.7)/PRF(×0.6) expansion rows to the TAIL unsorted and never sorts them —
569
+ // so a sparse variant (common in deep search, the vocabulary-mismatch path) would hand
570
+ // a tail-ranked expansion row to RRF at a worse rank than its score earns.
571
+ //
572
+ // THIS SORT IS NOW THE ONLY ONE. It used to be described as mirroring an in-engine sort
573
+ // that guarded the vector-RRF merge; that sort lived inside the vector block Phase-2
574
+ // deleted, so nothing upstream re-orders the list any more. Read the sentence that way
575
+ // before deleting this line as redundant — it is load-bearing, not a mirror.
575
576
  list.sort((a, b) => (a.score ?? 0) - (b.score ?? 0));
576
577
  return list;
577
578
  });
package/hook-llm.mjs CHANGED
@@ -25,12 +25,9 @@ import {
25
25
  import { acquireLLMSlot, releaseLLMSlot } from './hook-semaphore.mjs';
26
26
  import { BG_LLM_TIMEOUT_MS } from './haiku-client.mjs';
27
27
  import { scrubRecord } from './lib/scrub-record.mjs';
28
- import { vecTextForRow } from './tfidf.mjs';
29
28
  import {
30
29
  insertObservationRow,
31
30
  insertObservationFiles,
32
- insertObservationVector,
33
- upsertObservationVector,
34
31
  normalizeScope,
35
32
  SCOPE_PROMPT_LEGEND,
36
33
  } from './lib/observation-write.mjs';
@@ -210,22 +207,6 @@ function buildFtsTextField(obs) {
210
207
  };
211
208
  }
212
209
 
213
- // TF-IDF vector text. Must mirror the FTS-searchable content so the vector arm and
214
- // the BM25 arm rank on the same signal — including lesson_learned (highest FTS
215
- // weight) and search_aliases (finding #8: previously omitted, so even with vectors
216
- // enabled the paraphrase-bridge alias terms were invisible to cosine similarity).
217
- export function buildVecText(obs) {
218
- // Single source (V-F1): map the camelCase obs onto vecTextForRow's row shape so save and
219
- // every rebuild path encode the identical field set (title/narrative/concepts/lesson/aliases).
220
- return vecTextForRow({
221
- title: obs.title,
222
- narrative: obs.narrative,
223
- concepts: obs.concepts,
224
- lesson_learned: obs.lessonLearned,
225
- search_aliases: obs.searchAliases,
226
- });
227
- }
228
-
229
210
  /**
230
211
  * Save an observation to the database with three-tier dedup.
231
212
  * @returns {number|null} The saved observation ID, or null if deduped.
@@ -370,7 +351,7 @@ export function saveObservation(obs, projectOverride, sessionIdOverride, externa
370
351
  search_aliases: obs.searchAliases || null,
371
352
  });
372
353
 
373
- // Atomic: observation INSERT + observation_files + vector in one transaction.
354
+ // Atomic: observation INSERT + observation_files in one transaction.
374
355
  // Column list single-sourced in lib/observation-write (shared with manual mem_save).
375
356
  const savedId = db.transaction(() => {
376
357
  const id = insertObservationRow(db, {
@@ -399,7 +380,6 @@ export function saveObservation(obs, projectOverride, sessionIdOverride, externa
399
380
  });
400
381
 
401
382
  insertObservationFiles(db, id, obs.files);
402
- insertObservationVector(db, id, buildVecText(obs));
403
383
 
404
384
  return id;
405
385
  })();
@@ -1218,7 +1198,7 @@ ${actionList}`;
1218
1198
  debugLog('DEBUG', 'llm-episode', `upgrade-delete: obs #${episode.savedId} → event #${savedId}`);
1219
1199
  } else {
1220
1200
  // Non-event type (e.g. `change`) — upgrade pre-saved observations row in place
1221
- // so the enriched FTS text field + minhash + vector are refreshed atomically.
1201
+ // so the enriched FTS text field + minhash are refreshed atomically.
1222
1202
  const { conceptsText, factsText, textField } = buildFtsTextField(obs);
1223
1203
  const minhashSig = computeMinHash((obs.title || '') + ' ' + (obs.narrative || ''));
1224
1204
  // Scrub LLM-output text fields at the UPDATE boundary, mirroring the
@@ -1289,22 +1269,6 @@ ${actionList}`;
1289
1269
  savedId = episode.savedId;
1290
1270
  savedTable = 'observations';
1291
1271
  debugLog('DEBUG', 'llm-episode', `upgraded pre-saved obs #${savedId}`);
1292
-
1293
- // Update TF-IDF vector with enriched content. SQL + text derivation are
1294
- // lib/observation-write.mjs's (audit 2026-09-02 P1-4); `gate: false` keeps this
1295
- // path's prior behaviour, which never consulted vectorsEnabled().
1296
- upsertObservationVector(
1297
- db,
1298
- savedId,
1299
- {
1300
- title: obs.title,
1301
- narrative: obs.narrative,
1302
- concepts: conceptsText,
1303
- lesson_learned: safe.lesson_learned,
1304
- search_aliases: safe.search_aliases,
1305
- },
1306
- { gate: false, scope: 'handleLLMEpisode-vector' },
1307
- );
1308
1272
  }
1309
1273
  }
1310
1274
  } else {