claude_memory 0.12.0 → 0.13.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.
- checksums.yaml +4 -4
- data/.claude/memory.sqlite3 +0 -0
- data/.claude/rules/claude_memory.generated.md +44 -48
- data/.claude/settings.local.json +2 -1
- data/.claude-plugin/marketplace.json +2 -2
- data/.claude-plugin/plugin.json +3 -5
- data/CHANGELOG.md +52 -0
- data/CLAUDE.md +13 -8
- data/README.md +46 -0
- data/db/migrations/019_add_observations.rb +43 -0
- data/db/migrations/020_add_observation_promotion.rb +33 -0
- data/docs/GETTING_STARTED.md +38 -0
- data/docs/api_stability.md +23 -7
- data/docs/architecture.md +18 -6
- data/docs/audit_runbook.md +67 -0
- data/docs/dashboard.md +28 -0
- data/docs/improvements.md +94 -1
- data/docs/influence/mastra-observational-memory.md +198 -0
- data/docs/influence/strands-agent-sops.md +163 -0
- data/docs/quality_review.md +45 -0
- data/docs/soak/audit_2026-06-03_agent-training-program.json +53 -0
- data/docs/soak/audit_2026-06-03_agentic.json +31 -0
- data/docs/soak/audit_2026-06-03_ai-software-architect.json +19 -0
- data/docs/soak/audit_2026-06-03_chaos_to_the_rescue.json +60 -0
- data/docs/soak/audit_2026-06-03_claude_memory.json +55 -0
- data/docs/soak/audit_2026-06-03_daily-vibe.json +59 -0
- data/docs/soak/audit_2026-06-03_minerva-sky.json +19 -0
- data/docs/soak/audit_2026-06-03_nowreading.dev.json +19 -0
- data/docs/soak/audit_2026-06-03_ups.dev.json +55 -0
- data/docs/soak/baseline_2026-06-03.md +145 -0
- data/lib/claude_memory/audit/checks.rb +149 -0
- data/lib/claude_memory/audit/runner.rb +4 -0
- data/lib/claude_memory/commands/census_command.rb +1 -1
- data/lib/claude_memory/commands/checks/embeddings_check.rb +97 -0
- data/lib/claude_memory/commands/doctor_command.rb +1 -0
- data/lib/claude_memory/commands/hook_command.rb +16 -3
- data/lib/claude_memory/commands/initializers/hooks_configurator.rb +3 -1
- data/lib/claude_memory/commands/install_skill_command.rb +4 -0
- data/lib/claude_memory/commands/observations_command.rb +367 -0
- data/lib/claude_memory/commands/registry.rb +2 -0
- data/lib/claude_memory/commands/setup_vectors_command.rb +182 -0
- data/lib/claude_memory/commands/skills/reflect.md +68 -0
- data/lib/claude_memory/commands/stats_command.rb +60 -1
- data/lib/claude_memory/dashboard/api.rb +4 -0
- data/lib/claude_memory/dashboard/index.html +154 -2
- data/lib/claude_memory/dashboard/observations.rb +115 -0
- data/lib/claude_memory/dashboard/server.rb +1 -0
- data/lib/claude_memory/distill/extraction.rb +6 -4
- data/lib/claude_memory/distill/null_distiller.rb +86 -3
- data/lib/claude_memory/distill/reference_material_detector.rb +4 -1
- data/lib/claude_memory/domain/observation.rb +118 -0
- data/lib/claude_memory/embeddings/generator.rb +1 -1
- data/lib/claude_memory/hook/context_injector.rb +100 -2
- data/lib/claude_memory/mcp/handlers/management_handlers.rb +113 -2
- data/lib/claude_memory/mcp/handlers/query_handlers.rb +48 -1
- data/lib/claude_memory/mcp/instructions_builder.rb +1 -0
- data/lib/claude_memory/mcp/query_guide.rb +28 -0
- data/lib/claude_memory/mcp/tool_definitions.rb +58 -0
- data/lib/claude_memory/mcp/tools.rb +3 -0
- data/lib/claude_memory/observe/observations_renderer.rb +49 -0
- data/lib/claude_memory/observe/reflector.rb +91 -0
- data/lib/claude_memory/publish.rb +53 -1
- data/lib/claude_memory/resolve/resolver.rb +45 -8
- data/lib/claude_memory/store/schema_manager.rb +1 -1
- data/lib/claude_memory/store/sqlite_store.rb +181 -0
- data/lib/claude_memory/sweep/maintenance.rb +15 -1
- data/lib/claude_memory/sweep/sweeper.rb +7 -1
- data/lib/claude_memory/version.rb +1 -1
- data/lib/claude_memory.rb +7 -0
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93e553fc87bd36ebe27b8709a28dea9bd206a669491327fc8b6340e6078a8f67
|
|
4
|
+
data.tar.gz: c19722c5028ed5746f58792bb34e57939600deaa0a59b6ff00a421b3ed829bdc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7bd3a98c636c525e2bfc579e9e265ecef3a07311d4ba543d692abc092ddc0822df86832b9e5b53d802597a6dbe7495aa192210d6d4e5961c21383daac22cef09
|
|
7
|
+
data.tar.gz: b587239c11c342551f35937ecdbb108254269306fa98fbaac6680d23f8e427a32f26cbb1524c8f19a08da1773e292b6d75df92e101cb724d59fd65017f275832
|
data/.claude/memory.sqlite3
CHANGED
|
Binary file
|
|
@@ -1,69 +1,65 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
This file is auto-generated by claude-memory.
|
|
3
3
|
Do not edit manually - changes will be overwritten.
|
|
4
|
-
Generated: 2026-06-
|
|
4
|
+
Generated: 2026-06-16T18:31:16Z
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
7
|
# Project Memory
|
|
8
8
|
|
|
9
9
|
## Current Decisions
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- From
|
|
13
|
-
- From
|
|
11
|
+
- From Mastra Observational Memory study: add an episodic observation layer that augments (not replaces) the dynamic-recall semantic fact store — because facts answer 'what is true' and observations answer 'what happened', and claude_memory currently lacks the episodic half; recall stays for targeted lookups.
|
|
12
|
+
- From Mastra Observational Memory study: make observation reflection automatic via the PreCompact and SessionEnd hooks rather than a manual-only skill — because Claude Code exposes no timer/cron hook, but PreCompact fires on context pressure (the analog of Mastra's token threshold) and rides the existing session at no extra API cost.
|
|
13
|
+
- From Mastra Observational Memory study: run the Reflector's deterministic GC shell-side in Ruby and its semantic consolidation via PreCompact additionalContext (Claude-as-reflector inline) — to keep automatic reflection within the no-extra-API-cost convention, explicitly rejecting Claude Code Routines and subagents because each incurs a separate token budget.
|
|
14
|
+
- From Mastra Observational Memory study: tombstone superseded observations via a consolidated_into link rather than hard-deleting them (unlike Mastra's lossy drop) — to preserve claude_memory's provenance guarantee while still bounding context size.
|
|
15
|
+
- From Mastra Observational Memory study: promote an observation to a structured fact only after corroboration across multiple observations — because requiring repeated sightings before commitment doubles as an anti-hallucination gate against reject-churn from one-off doc/example text.
|
|
16
|
+
- MCP tool-call telemetry stores minimal columns (tool_name, duration_ms, result_count, scope, error_class) — deliberately no query_text or query_hash. YAGNI: hashes are write-only without the raw text, and raw text adds privacy concerns without clear value beyond existing shortcut tools (memory.decisions, memory.conventions). — to avoid storing query hashes that are write-only without the raw text.
|
|
17
|
+
- From QMD 2026-02-02 restudy: adopt Claude Code plugin format, MCP structured content pattern, MCP query guide prompt, inline status checks. Carry forward sqlite-vec, RRF, docids, smart expansion from 2026-01-26. Reject custom fine-tuned models, LLM reranking, YAML collections. — to align with our pure-Ruby/SQLite constraints (no GGUF, no LLM-side fine-tuning).
|
|
18
|
+
- From claude-supermemory study: adopt SessionStart hook context injection (hookSpecificOutput.additionalContext), tool-specific observation compression, and relative time formatting. Reject cloud storage dependency and no-test approach. — to avoid cloud-storage lock-in and no-test fragility identified in the study.
|
|
14
19
|
|
|
15
20
|
## Conventions
|
|
16
21
|
|
|
17
|
-
- A/B testing methodology for memory plugin evaluation — How to test with/without memory using claude CLI — --plugin-dir doesn't work with --bare, use --mcp-config instead — To A/B test memory's impact on Claude Code responses: (imported from project auto-memory; see source file for full reasoning)
|
|
18
|
-
- do...end blocks over braces when args repeat or block is non-trivial — Block syntax preference for multi-argument or multi-expression blocks — When a block call has repeated argument names, multiple expressions, or reads awkwardly on one line, use `do...end` form rather than `{ }`. One-liners with simple single-expression bodies and short argument lists are fine with braces. (imported from project auto-memory; see source file for full reasoning)
|
|
19
|
-
- Always commit .claude/memory.sqlite3 — Per user direction (2026-05-21), .claude/memory.sqlite3 should be staged and committed alongside any change that updates project memory — even though it's a binary SQLite DB with WAL artifacts. — Always include `.claude/memory.sqlite3` in commits that touch project memory or knowledge. (imported from project auto-memory; see source file for full reasoning)
|
|
22
|
+
- A/B testing methodology for memory plugin evaluation — How to test with/without memory using claude CLI — --plugin-dir doesn't work with --bare, use --mcp-config instead — To A/B test memory's impact on Claude Code responses: (imported from project auto-memory; see source file for full reasoning) — to enable head-to-head measurement of memory's impact on Claude Code responses.
|
|
23
|
+
- do...end blocks over braces when args repeat or block is non-trivial — Block syntax preference for multi-argument or multi-expression blocks — When a block call has repeated argument names, multiple expressions, or reads awkwardly on one line, use `do...end` form rather than `{ }`. One-liners with simple single-expression bodies and short argument lists are fine with braces. (imported from project auto-memory; see source file for full reasoning) — to keep multi-line blocks readable and avoid brace/multi-expression awkwardness.
|
|
24
|
+
- Always commit .claude/memory.sqlite3 — Per user direction (2026-05-21), .claude/memory.sqlite3 should be staged and committed alongside any change that updates project memory — even though it's a binary SQLite DB with WAL artifacts. — Always include `.claude/memory.sqlite3` in commits that touch project memory or knowledge. (imported from project auto-memory; see source file for full reasoning) — to ensure project memory is reproducible across collaborators.
|
|
20
25
|
- Commit workflow preferences — How the user prefers commits to be structured and when to make them — Wait for the user to ask for commits — don't commit proactively. When asked, group changes into logical atomic commits: (imported from project auto-memory; see source file for full reasoning)
|
|
21
|
-
- Data-driven analysis before design changes — User expects thorough multi-project data surveys and critical questioning of assumptions before committing to architectural changes — When proposing design changes (especially to schemas, vocabularies, or policies), gather real usage data first and present a critical analysis before implementing. (imported from project auto-memory; see source file for full reasoning)
|
|
22
|
-
- Fix hallucination triggers at the source, not via repeated reject churn — When the distiller repeatedly produces the same wrong fact, trace it to the CLAUDE.md / docs example text; fixing the source stops re-appearance — When the dashboard's Conflicts tab accumulates clusters of the same kind of bad fact (e.g. many `uses_database` contradictions against `sqlite`), the root cause is almost always **example text in documentation** that the distiller is interpreting as a literal claim about the current repo. Single-value predicates (`uses_database`, `deployment_platform`, `auth_method`) are especially vulnerable becau... (imported from project auto-memory; see source file for full reasoning)
|
|
23
|
-
- Hooks run the installed gem, not the working copy — always `rake install` after editing hook/MCP code — .claude/settings.json hooks invoke `claude-memory` via PATH, so changes on a branch only take effect after `bundle exec rake install` — `.claude/settings.json` hooks call bare `claude-memory hook ingest` / `claude-memory hook context` / etc. That resolves via PATH to the installed gem, not the working-copy `./exe/claude-memory`. After editing any hook/MCP/distiller code on a branch, the change does NOT reach Claude Code until `bundle exec rake install` rebuilds and reinstalls the gem (which overwrites the prior install at the same ... (imported from project auto-memory; see source file for full reasoning)
|
|
24
|
-
- No extra API costs for features — User strongly prefers using Claude Code itself (skills, context hooks) over separate API calls that cost extra money — Do not add features that require separate Anthropic API calls (e.g., via anthropic-rb gem) when Claude Code itself can perform the same task. Use skills, context hook injection, and MCP tools to leverage the existing Claude Code session instead. (imported from project auto-memory; see source file for full reasoning)
|
|
25
|
-
- Quality review update cycle — Keep quality_review.md current as items are resolved — don't let it drift — When completing quality review items, update `docs/quality_review.md` immediately: (imported from project auto-memory; see source file for full reasoning)
|
|
26
|
+
- Data-driven analysis before design changes — User expects thorough multi-project data surveys and critical questioning of assumptions before committing to architectural changes — When proposing design changes (especially to schemas, vocabularies, or policies), gather real usage data first and present a critical analysis before implementing. (imported from project auto-memory; see source file for full reasoning) — to avoid premature architectural commitments before real usage data is in hand.
|
|
27
|
+
- Fix hallucination triggers at the source, not via repeated reject churn — When the distiller repeatedly produces the same wrong fact, trace it to the CLAUDE.md / docs example text; fixing the source stops re-appearance — When the dashboard's Conflicts tab accumulates clusters of the same kind of bad fact (e.g. many `uses_database` contradictions against `sqlite`), the root cause is almost always **example text in documentation** that the distiller is interpreting as a literal claim about the current repo. Single-value predicates (`uses_database`, `deployment_platform`, `auth_method`) are especially vulnerable becau... (imported from project auto-memory; see source file for full reasoning) — because reject-only is treating symptoms; the root cause is doc/example text the distiller mistakes for project claims.
|
|
28
|
+
- Hooks run the installed gem, not the working copy — always `rake install` after editing hook/MCP code — .claude/settings.json hooks invoke `claude-memory` via PATH, so changes on a branch only take effect after `bundle exec rake install` — `.claude/settings.json` hooks call bare `claude-memory hook ingest` / `claude-memory hook context` / etc. That resolves via PATH to the installed gem, not the working-copy `./exe/claude-memory`. After editing any hook/MCP/distiller code on a branch, the change does NOT reach Claude Code until `bundle exec rake install` rebuilds and reinstalls the gem (which overwrites the prior install at the same ... (imported from project auto-memory; see source file for full reasoning) — to ensure code changes reach the production hooks, which invoke the installed gem via PATH.
|
|
29
|
+
- No extra API costs for features — User strongly prefers using Claude Code itself (skills, context hooks) over separate API calls that cost extra money — Do not add features that require separate Anthropic API calls (e.g., via anthropic-rb gem) when Claude Code itself can perform the same task. Use skills, context hook injection, and MCP tools to leverage the existing Claude Code session instead. (imported from project auto-memory; see source file for full reasoning) — to avoid hidden ongoing spend that bypasses the Claude Code session's existing budget.
|
|
30
|
+
- Quality review update cycle — Keep quality_review.md current as items are resolved — don't let it drift — When completing quality review items, update `docs/quality_review.md` immediately: (imported from project auto-memory; see source file for full reasoning) — to prevent quality_review.md from drifting out of date.
|
|
26
31
|
- Refactoring approach preferences — How to approach god object extraction and structural refactoring in this codebase — Use module inclusion (not class extraction) when breaking up god objects. Include modules directly into the existing class so the public API is unchanged and zero tests need modification. This was validated three times:
|
|
27
|
-
- Round-trip migration specs cover each prior release boundary — For pre-release prep, write end-to-end migration specs from every distinct schema boundary back through ~3 prior releases — Before cutting a release that includes migrations, add round-trip specs that fixture an older DB at each distinct prior release's schema version, open via `SQLiteStore.new`, and assert the full upgrade path: schema_info advancement, data preservation across entities/facts/content_items/provenance, additive table/column creation, predicate-rewrite effects where applicable, and idempotency on re-open... (imported from project auto-memory; see source file for full reasoning)
|
|
32
|
+
- Round-trip migration specs cover each prior release boundary — For pre-release prep, write end-to-end migration specs from every distinct schema boundary back through ~3 prior releases — Before cutting a release that includes migrations, add round-trip specs that fixture an older DB at each distinct prior release's schema version, open via `SQLiteStore.new`, and assert the full upgrade path: schema_info advancement, data preservation across entities/facts/content_items/provenance, additive table/column creation, predicate-rewrite effects where applicable, and idempotency on re-open... (imported from project auto-memory; see source file for full reasoning) — to ensure migrations remain forward-compatible across release boundaries.
|
|
28
33
|
- Codify behavioral contracts in tests, not just comments — When code has a deliberate scope limitation (one-shot, advisory-only, intentionally non-idempotent), write a test that fails if someone "fixes" it into being more general — When code has a deliberate scope limitation — a one-shot data migration, an advisory-only field, a method intentionally not idempotent for new inputs — write a test that exercises a scenario which would *break* if someone tried to make it more general. (imported from project auto-memory; see source file for full reasoning)
|
|
29
|
-
- Treat UX gaps as architecture smells — user inspection/debugging questions expose god classes and missing abstractions — When users ask "can I see/debug/act on X in the dashboard?", the answer is almost always "we need a new class or route, not a new button" — Across three architectural reviews in the 2026-04-17 → 20 session, every concrete UX gap the user identified traced back to a structural issue the code already had, not a frontend-only fix. Treating critique as a forcing function for refactoring produced cleaner results than either extracting preemptively (premature) or patching only the surface (symptom). (imported from project auto-memory; see source file for full reasoning)
|
|
30
|
-
- "database disk image is malformed" from FTS5 `ORDER BY rank` after sqlite3 .recover — sqlite3 .recover restores rows but can leave contentless FTS5 auxiliary indexes in a state where basic MATCH works but ORDER BY rank throws "malformed"; fix is `claude-memory compact` to rebuild the FTS index — A DB recovered via `sqlite3 corrupt.db .recover > dump.sql && sqlite3 fresh.db < dump.sql` can end up with an FTS5 index that's *partially* functional: (imported from project auto-memory; see source file for full reasoning)
|
|
31
|
-
- `rake install` uses `git ls-files`; untracked files silently disappear from the gem — Running `bundle exec rake install` before staging new files produces a gem missing those files, causing LoadError in hooks and MCP server — The claude_memory gemspec builds its file list via `IO.popen(%w[git ls-files -z], ...)` (claude_memory.gemspec:24). Any file that hasn't been `git add`ed at build time is **invisible to the gem** even though it exists on disk. The local working copy keeps running fine (dashboard server uses `./exe/claude-memory` against the repo directly), but the installed gem at `~/.gem/ruby/*/gems/claude_memory-... (imported from project auto-memory; see source file for full reasoning)
|
|
32
|
-
- Distiller scope_hint is advisory, not a routing signal — NullDistiller emits scope_hint: "global" for text matching GLOBAL_SCOPE_PATTERNS, but the resolver never routes writes between stores — scope_hint must not override fact.scope — `Distill::NullDistiller#global_scope_signal?` matches text like "always" / "my preference" / "in all projects" and stamps `scope_hint: "global"` on every fact extracted from that text. The hint is advisory metadata for downstream promotion decisions. It is NOT a routing signal — the resolver writes to whichever `SQLiteStore` was injected into it (always the project DB in the normal ingest path), re... (imported from project auto-memory; see source file for full reasoning)
|
|
33
|
-
- Sequel DB reads must use the extralite adapter — Opening a SQLite DB for ad-hoc reads requires the extralite adapter URI; Sequel.sqlite silently depends on an ungem'd sqlite3 — Never use `Sequel.sqlite(db_path)` or `Sequel.sqlite(db_path, readonly: true)` in this codebase. The gemspec lists only `extralite (~> 2.14)` — it does **not** depend on the `sqlite3` gem. `Sequel.sqlite` routes through Sequel's `sqlite` adapter which requires `gem "sqlite3"` and will raise `Sequel::AdapterNotFound: LoadError: cannot load such file -- sqlite3` at runtime. (imported from project auto-memory; see source file for full reasoning)
|
|
34
|
-
- Never `git checkout --` an active SQLite DB with WAL mode — Using `git checkout --` on .claude/memory.sqlite3 while readers/writers are open corrupts the DB via WAL/main file mismatch — Never run `git checkout -- .claude/memory.sqlite3` (or any SQLite DB in WAL mode) while any process has it open. Git replaces only the main DB file, leaving the WAL/SHM sidecar files referencing pages that no longer exist in the replaced file. Next read → "Extralite::Error: database disk image is malformed" and integrity_check shows btree errors across multiple trees. (imported from project auto-memory; see source file for full reasoning)
|
|
35
|
-
- SQLiteStore silently creates in-memory DB for relative paths — `SQLiteStore.new('.claude/memory.sqlite3')` with a relative path opens an empty in-memory DB, not the file — always pass absolute paths in tests/probes — `SQLiteStore.new(path)` builds a Sequel URI as `extralite:#{path}`. With a relative path like `.claude/memory.sqlite3`, the resulting URI `extralite:.claude/memory.sqlite3` is parsed with an empty database component, so Extralite opens an in-memory database. Schema migrations run against the in-memory DB (so `schema_version` reports the current version), but ALL queries return 0 rows and the real f... (imported from project auto-memory; see source file for full reasoning)
|
|
36
|
-
- Two tool_calls tables exist — don't conflate them — tool_calls (v3) stores transcript-observed Claude Code tool usage; mcp_tool_calls (v13) stores MCP server telemetry — There are **two** tables with similar names serving different purposes: (imported from project auto-memory; see source file for full reasoning)
|
|
37
|
-
- Distiller hallucination from CLAUDE.md example text — The scope-system example in CLAUDE.md causes recurring false fact extraction — reject + re-ingest creates rejection churn — CLAUDE.md contains a scope-system explanation with example text: (imported from project auto-memory; see source file for full reasoning)
|
|
38
|
-
- PredicatePolicy is the single source of truth for predicate vocabulary — All predicate knowledge (vocabulary, cardinality, sections, synonyms, LLM guidance) derives from PredicatePolicy — never hardcode predicate names elsewhere — As of 0.9.0, `PredicatePolicy` in `lib/claude_memory/resolve/predicate_policy.rb` is the authoritative source for all predicate-related behavior. This was a deliberate consolidation after finding the same predicate list duplicated in 4 files that drifted independently. (imported from project auto-memory; see source file for full reasoning)
|
|
34
|
+
- Treat UX gaps as architecture smells — user inspection/debugging questions expose god classes and missing abstractions — When users ask "can I see/debug/act on X in the dashboard?", the answer is almost always "we need a new class or route, not a new button" — Across three architectural reviews in the 2026-04-17 → 20 session, every concrete UX gap the user identified traced back to a structural issue the code already had, not a frontend-only fix. Treating critique as a forcing function for refactoring produced cleaner results than either extracting preemptively (premature) or patching only the surface (symptom). (imported from project auto-memory; see source file for full reasoning) — because frontend patches usually mask structural debt that surfaces later.
|
|
35
|
+
- "database disk image is malformed" from FTS5 `ORDER BY rank` after sqlite3 .recover — sqlite3 .recover restores rows but can leave contentless FTS5 auxiliary indexes in a state where basic MATCH works but ORDER BY rank throws "malformed"; fix is `claude-memory compact` to rebuild the FTS index — A DB recovered via `sqlite3 corrupt.db .recover > dump.sql && sqlite3 fresh.db < dump.sql` can end up with an FTS5 index that's *partially* functional: (imported from project auto-memory; see source file for full reasoning) — fix is claude-memory compact to rebuild the FTS index without doing another .recover.
|
|
36
|
+
- `rake install` uses `git ls-files`; untracked files silently disappear from the gem — Running `bundle exec rake install` before staging new files produces a gem missing those files, causing LoadError in hooks and MCP server — The claude_memory gemspec builds its file list via `IO.popen(%w[git ls-files -z], ...)` (claude_memory.gemspec:24). Any file that hasn't been `git add`ed at build time is **invisible to the gem** even though it exists on disk. The local working copy keeps running fine (dashboard server uses `./exe/claude-memory` against the repo directly), but the installed gem at `~/.gem/ruby/*/gems/claude_memory-... (imported from project auto-memory; see source file for full reasoning) — to ensure new files reach the installed gem (the gemspec uses git ls-files for the manifest).
|
|
37
|
+
- Distiller scope_hint is advisory, not a routing signal — NullDistiller emits scope_hint: "global" for text matching GLOBAL_SCOPE_PATTERNS, but the resolver never routes writes between stores — scope_hint must not override fact.scope — `Distill::NullDistiller#global_scope_signal?` matches text like "always" / "my preference" / "in all projects" and stamps `scope_hint: "global"` on every fact extracted from that text. The hint is advisory metadata for downstream promotion decisions. It is NOT a routing signal — the resolver writes to whichever `SQLiteStore` was injected into it (always the project DB in the normal ingest path), re... (imported from project auto-memory; see source file for full reasoning) — to prevent scope drift between resolver-determined store and distiller-stamped hint.
|
|
38
|
+
- Sequel DB reads must use the extralite adapter — Opening a SQLite DB for ad-hoc reads requires the extralite adapter URI; Sequel.sqlite silently depends on an ungem'd sqlite3 — Never use `Sequel.sqlite(db_path)` or `Sequel.sqlite(db_path, readonly: true)` in this codebase. The gemspec lists only `extralite (~> 2.14)` — it does **not** depend on the `sqlite3` gem. `Sequel.sqlite` routes through Sequel's `sqlite` adapter which requires `gem "sqlite3"` and will raise `Sequel::AdapterNotFound: LoadError: cannot load such file -- sqlite3` at runtime. (imported from project auto-memory; see source file for full reasoning) — to avoid Sequel::AdapterNotFound at runtime (this gem doesn't depend on the sqlite3 adapter).
|
|
39
|
+
- Never `git checkout --` an active SQLite DB with WAL mode — Using `git checkout --` on .claude/memory.sqlite3 while readers/writers are open corrupts the DB via WAL/main file mismatch — Never run `git checkout -- .claude/memory.sqlite3` (or any SQLite DB in WAL mode) while any process has it open. Git replaces only the main DB file, leaving the WAL/SHM sidecar files referencing pages that no longer exist in the replaced file. Next read → "Extralite::Error: database disk image is malformed" and integrity_check shows btree errors across multiple trees. (imported from project auto-memory; see source file for full reasoning) — to avoid WAL/main file mismatch corruption.
|
|
40
|
+
- SQLiteStore silently creates in-memory DB for relative paths — `SQLiteStore.new('.claude/memory.sqlite3')` with a relative path opens an empty in-memory DB, not the file — always pass absolute paths in tests/probes — `SQLiteStore.new(path)` builds a Sequel URI as `extralite:#{path}`. With a relative path like `.claude/memory.sqlite3`, the resulting URI `extralite:.claude/memory.sqlite3` is parsed with an empty database component, so Extralite opens an in-memory database. Schema migrations run against the in-memory DB (so `schema_version` reports the current version), but ALL queries return 0 rows and the real f... (imported from project auto-memory; see source file for full reasoning) — to ensure SQLiteStore opens the real file rather than a silent in-memory shadow.
|
|
41
|
+
- Two tool_calls tables exist — don't conflate them — tool_calls (v3) stores transcript-observed Claude Code tool usage; mcp_tool_calls (v13) stores MCP server telemetry — There are **two** tables with similar names serving different purposes: (imported from project auto-memory; see source file for full reasoning) — to avoid joining unrelated rows from disjoint telemetry tables.
|
|
42
|
+
- Distiller hallucination from CLAUDE.md example text — The scope-system example in CLAUDE.md causes recurring false fact extraction — reject + re-ingest creates rejection churn — CLAUDE.md contains a scope-system explanation with example text: (imported from project auto-memory; see source file for full reasoning) — to prevent re-extraction churn from documentation example text that the distiller takes literally.
|
|
43
|
+
- PredicatePolicy is the single source of truth for predicate vocabulary — All predicate knowledge (vocabulary, cardinality, sections, synonyms, LLM guidance) derives from PredicatePolicy — never hardcode predicate names elsewhere — As of 0.9.0, `PredicatePolicy` in `lib/claude_memory/resolve/predicate_policy.rb` is the authoritative source for all predicate-related behavior. This was a deliberate consolidation after finding the same predicate list duplicated in 4 files that drifted independently. (imported from project auto-memory; see source file for full reasoning) — to avoid divergent predicate lists across files that drifted independently before consolidation.
|
|
39
44
|
- Hook-telemetry features need a manual hook trigger to verify in production, not just specs — `bundle exec rake install` AND fire a real hook AND check `sqlite3 ... json_extract(detail_json, '$.<field>')`, because specs assert against working-tree code but `.claude/settings.json` hooks invoke the installed gem via PATH so the asserted field can be silently absent in production. Hit on 2026-04-30 shipping #47 token-budget telemetry: 156 specs green but `context_tokens` was missing from 24h of real activity_events.
|
|
40
|
-
- When verifying any new field on activity_events.detail_json, the canonical smoke test is: `echo '{"hook_event_name":"SessionStart","session_id":"smoketest","source":"startup","cwd":"$(pwd)"}' | claude-memory hook context` then inspect via `sqlite3 .claude/memory.sqlite3 "SELECT json_extract(detail_json, '$.<field>') FROM activity_events WHERE event_type='hook_context' ORDER BY id DESC LIMIT 1"`. If null after rake install, the installed gem code hasn't picked up the change.
|
|
45
|
+
- When verifying any new field on activity_events.detail_json, the canonical smoke test is: `echo '{"hook_event_name":"SessionStart","session_id":"smoketest","source":"startup","cwd":"$(pwd)"}' | claude-memory hook context` then inspect via `sqlite3 .claude/memory.sqlite3 "SELECT json_extract(detail_json, '$.<field>') FROM activity_events WHERE event_type='hook_context' ORDER BY id DESC LIMIT 1"`. If null after rake install, the installed gem code hasn't picked up the change. — to ensure the installed gem actually picks up new detail_json fields.
|
|
41
46
|
- Treat UX gaps as architecture smells: when a user asks "can I see/debug/act on X in the dashboard?" the answer is almost always a missing class or route, not a new button. Every UX critique in this project's dashboard work traced to a structural gap — god-class growth, four drifting fact serializers, scope_hint as silent scope override, no fact detail endpoint. Pattern: the surface question is usually a router into the architecture. Before reaching for frontend fixes, ask "what server-side data shape would make this easy?" — if that shape doesn't exist cleanly, treat it as the real work. Commit refactor separately from feature it enables ([Refactor]/[Feature]/[Fix] prefixes) so the critique→structural fix→UX fix chain is visible in git log.
|
|
42
47
|
- Four-surface staleness: after any change that touches UI + backend + plugin-launched binaries, refresh all four or the change looks broken. (1) bundle exec rake install so the installed gem catches up (hooks + MCP launched by Claude Code run from PATH). (2) Ctrl-C and re-run ./exe/claude-memory dashboard — server is long-lived Ruby, no live-reload. (3) /mcp reconnect in Claude Code so the MCP subprocess respawns. (4) Hard-refresh browser (Cmd-Shift-R) so cached index.html JS reloads. Skipping any produces confusing "my fix doesn't work." rspec green does NOT mean end-to-end works; before declaring UI-affecting changes done, curl the endpoint and verify shape matches frontend expectation. curl alt-port dashboard (--port 3388 --no-open in background) is fastest smoke test without disturbing user's running dashboard.
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- Version must be updated in three places: lib/claude_memory/version.rb, .claude-plugin/plugin.json, .claude-plugin/marketplace.json. Runtime code uses ClaudeMemory::VERSION dynamically.
|
|
59
|
-
- Use module inclusion (not class extraction) to break up god objects — preserves public API so zero tests need modification
|
|
60
|
-
- Configuration class has instance methods only — use Configuration.new.global_db_path, not Configuration.global_db_path. Stub with instance_double + allow(Configuration).to receive(:new).and_return(config)
|
|
61
|
-
- OperationTracker.reset_stuck_operations only resets operations older than 24 hours (STALE_THRESHOLD_SECONDS). Tests must backdate started_at to trigger resets.
|
|
62
|
-
- SCHEMA_VERSION constant lives in Store::SchemaManager module but is accessible as SQLiteStore::SCHEMA_VERSION via Ruby include-based constant lookup
|
|
63
|
-
- MCP tools return dual content (text summary) + structuredContent (JSON) via TextSummary module and Server#handle_tools_call. Compact mode (compact: true) omits receipts for ~60% smaller responses.
|
|
64
|
-
- ContentSanitizer strips system-reminder, local-command-caveat, command-message, command-name, command-args tags in addition to private/no-memory/secret/claude-memory-context.
|
|
65
|
-
- Core::RelativeTime module provides progressive time formatting: just now → Xm ago → Xh ago → Xd ago → YYYY-MM-DD. Used in ResponseFormatter for *_ago fields.
|
|
66
|
-
- MCP server registers memory_guide prompt via prompts/list and prompts/get endpoints. QueryGuide module holds prompt content.
|
|
48
|
+
- MCP tool-call telemetry is recorded via MCP::Telemetry wrapping Server#handle_tools_call. Writes to mcp_tool_calls table in the project DB. Swallows DB errors so telemetry never breaks a real tool response. Viewable via 'claude-memory stats --tools [--since DAYS]'. — to ensure telemetry never breaks a real tool response (DB errors are swallowed).
|
|
49
|
+
- mcp_tool_calls retention is 90 days, enforced by Sweep::Maintenance#prune_old_mcp_tool_calls wired into Sweeper#run!. Configurable via mcp_tool_call_retention_days in Maintenance DEFAULT_CONFIG. — long enough to support quarterly tool-usage retrospectives without unbounded DB growth.
|
|
50
|
+
- CLAUDE_CONFIG_DIR env var overrides the default ~/.claude location for Configuration#global_db_path. Access via Configuration#claude_config_dir. Additive, backwards compatible. — to support test isolation and air-gapped installs that don't touch ~/.claude.
|
|
51
|
+
- Registry::COMMANDS stores {class:, description:} entries as the single source of truth for command name, class reference, and shell-completion description. Class constants stored directly (no const_get). Registry.descriptions feeds CompletionCommand; adding a new command requires updating only this hash. — to ensure adding a command requires only this hash update (no const_get, no description duplication).
|
|
52
|
+
- EXPECTED_HOOKS constant must stay in sync with events in HooksConfigurator#build_hooks_config. Adding a new hook event without updating EXPECTED_HOOKS causes false doctor warnings. — without this sync the doctor reports false hook-mismatch warnings.
|
|
53
|
+
- Tests using --db for hook context only set the project DB path. StoreManager still connects to the real global DB. Must stub Configuration to return a temp global path for isolation. — to ensure test isolation from the user's real global DB.
|
|
54
|
+
- Version must be updated in three places: lib/claude_memory/version.rb, .claude-plugin/plugin.json, .claude-plugin/marketplace.json. Runtime code uses ClaudeMemory::VERSION dynamically. — to ensure plugin/marketplace metadata matches the gem version on release.
|
|
55
|
+
- Use module inclusion (not class extraction) to break up god objects — preserves public API so zero tests need modification — to avoid breaking the public API and forcing test updates during god-object cleanup.
|
|
56
|
+
- Configuration class has instance methods only — use Configuration.new.global_db_path, not Configuration.global_db_path. Stub with instance_double + allow(Configuration).to receive(:new).and_return(config) — to make Configuration stubbable in tests via instance_double.
|
|
57
|
+
- OperationTracker.reset_stuck_operations only resets operations older than 24 hours (STALE_THRESHOLD_SECONDS). Tests must backdate started_at to trigger resets. — to prevent the reset from clobbering operations still legitimately in flight.
|
|
58
|
+
- SCHEMA_VERSION constant lives in Store::SchemaManager module but is accessible as SQLiteStore::SCHEMA_VERSION via Ruby include-based constant lookup — accessible via include because Ruby's constant lookup walks the inclusion chain.
|
|
59
|
+
- MCP tools return dual content (text summary) + structuredContent (JSON) via TextSummary module and Server#handle_tools_call. Compact mode (compact: true) omits receipts for ~60% smaller responses. — so that compact-mode callers pay ~60% fewer tokens per response.
|
|
60
|
+
- ContentSanitizer strips system-reminder, local-command-caveat, command-message, command-name, command-args tags in addition to private/no-memory/secret/claude-memory-context. — to prevent harness-internal tags from being ingested as user content.
|
|
61
|
+
- Core::RelativeTime module provides progressive time formatting: just now → Xm ago → Xh ago → Xd ago → YYYY-MM-DD. Used in ResponseFormatter for *_ago fields. — to make stale-fact warnings legible across both recent and old timestamps.
|
|
62
|
+
- MCP server registers memory_guide prompt via prompts/list and prompts/get endpoints. QueryGuide module holds prompt content. — to ensure memory_guide is discoverable via the standard MCP prompt registry.
|
|
67
63
|
|
|
68
64
|
## Technical Constraints
|
|
69
65
|
|
data/.claude/settings.local.json
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"plugins": [
|
|
8
8
|
{
|
|
9
9
|
"name": "claude-memory",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.13.0",
|
|
11
11
|
"source": "./",
|
|
12
|
-
"description": "Long-term memory for Claude Code. Recalls architecture, conventions, and decisions across sessions — so Claude explains your codebase without file traversal, follows your patterns, and never re-asks what it already learned.",
|
|
12
|
+
"description": "Long-term memory for Claude Code. Recalls architecture, conventions, and decisions across sessions, plus an episodic observation log of what happened — so Claude explains your codebase without file traversal, follows your patterns, learns from corrections, and never re-asks what it already learned.",
|
|
13
13
|
"repository": "https://github.com/codenamev/claude_memory"
|
|
14
14
|
}
|
|
15
15
|
]
|
data/.claude-plugin/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Long-term memory for Claude Code. Recalls architecture, conventions, and decisions across sessions — so Claude explains your codebase without file traversal, follows your patterns, and never re-asks what it already learned.",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Long-term memory for Claude Code. Recalls architecture, conventions, and decisions across sessions, plus an episodic observation log of what happened — so Claude explains your codebase without file traversal, follows your patterns, learns from corrections, and never re-asks what it already learned.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Valentino Stoll",
|
|
7
7
|
"email": "v@codenamev.com"
|
|
@@ -16,7 +16,5 @@
|
|
|
16
16
|
"args": []
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
|
-
"
|
|
20
|
-
"commands": "./commands/",
|
|
21
|
-
"outputStyles": "./output-styles/"
|
|
19
|
+
"commands": "./commands/"
|
|
22
20
|
}
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,58 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.13.0] - 2026-06-18
|
|
8
|
+
|
|
9
|
+
Theme: **Episodic memory — a second kind of memory.** ClaudeMemory gains an append-only *observation* layer ("what happened") that complements the semantic fact store ("what is true"), modeled on [Mastra's Observational Memory](docs/influence/mastra-observational-memory.md). Observations accrue automatically, are deduplicated/consolidated by reflection, and are promoted to facts only after corroboration — making repeated sighting an anti-hallucination gate built into the memory model. Schema advances to v20 (additive; no breaking changes to existing facts/queries).
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
**Episodic Observation Layer**
|
|
14
|
+
|
|
15
|
+
- **`observations` table** (schema v19–v20) — append-only episodic rows complementing facts. Columns include `body`, `kind`, `priority` (1=🔴/2=🟡/3=🟢), `scope`, `source_content_item_id` (provenance), `consolidated_into` (tombstone lineage — superseded observations are preserved, never deleted), `corroboration_count`, `promoted_at`/`promoted_fact_id`, `status`.
|
|
16
|
+
- **Observer** — Layer-1 `NullDistiller` (regex) plus Layer-2 Claude-as-observer (SessionStart context hook, zero extra API cost) emit observations alongside facts; persisted by the `Resolver` inside the existing extraction transaction.
|
|
17
|
+
- **Reflector** (`Observe::Reflector`) — deterministic dedup + TTL-expiry of info-level observations runs shell-side on `PreCompact`/`SessionEnd`; semantic consolidation (Claude-as-reflector) rides the next turn's `PreCompact` `additionalContext`. No separate API spend.
|
|
18
|
+
- **Two-block SessionStart injection** — Block 1 is the stable observation log (🔴-marked); Block 2 is the undistilled "pending knowledge" tail. Wrapped in `<claude-memory-context>` so the injected log isn't re-ingested as new observations.
|
|
19
|
+
- **Observation→fact promotion bridge** — facts are created from observations only after corroboration (≥2 sightings, `Domain::Observation::PROMOTION_THRESHOLD`); refuses uncorroborated or already-promoted rows.
|
|
20
|
+
- **MCP tools** (28 total): `memory.observations` (read the episodic log), `memory.promote_observation` (corroboration-gated promotion), `memory.consolidate_observations` (semantic merge; corroboration combines, sources tombstoned).
|
|
21
|
+
- **`/reflect` skill** — guided survey → consolidate → promote → report pass over the observation log.
|
|
22
|
+
- **`claude-memory observations`** command — list/inspect the log (counts by status/kind/priority, promotion readiness, compression), plus `promote` and `consolidate` subcommands; `claude-memory stats --observations` summarizes counts.
|
|
23
|
+
- **Dashboard Observations panel** (first-class, main sidebar + Advanced tab) — counts by kind/priority, corroboration + promotion readiness, source→observation compression ratio, recent timeline.
|
|
24
|
+
- **`claude-memory audit` observation health checks** — orphaned observations, promotion consistency, and tombstone-chain validity, documented in `docs/audit_runbook.md`.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **Dependencies updated** to current within constraints — `sequel` 5.105, `standard` 1.55, `rubocop` 1.87, `json`, `psych`, `parallel` 2.x, among others. No runtime-gem major bumps required.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **`consolidate_observations` read-modify-write race** — the source read and corroboration sum now run inside the transaction, with `status='active'` re-asserted on the tombstone update, so concurrent `PreCompact`/`SessionEnd` reflectors can't double-count or double-tombstone.
|
|
33
|
+
- **`go` language false positive** — the distiller no longer extracts the English word "go" as the Go language (case-sensitive match + `golang` normalization).
|
|
34
|
+
|
|
35
|
+
## [0.12.1] - 2026-06-05
|
|
36
|
+
|
|
37
|
+
Theme: **Upgrade-experience patches surfaced by the 0.12.0 soak.** Four small but high-impact fixes — all uncovered by one user upgrading a single project — closing visibility gaps in the doctor and the plugin manifest. No schema changes, no breaking changes.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **`claude-memory setup-vectors` command** — the documented opt-in path for end users who want vector recall via the BAAI/bge-small-en-v1.5 model. fastembed remains a dev/test gem dependency by design (the default install stays light); this command verifies the chosen provider is loadable (gracefully prompts to `gem install fastembed` if not), writes `CLAUDE_MEMORY_EMBEDDING_PROVIDER` (and optional `CLAUDE_MEMORY_EMBEDDING_MODEL`) to the project's `.claude/settings.json` env block — the same mechanism Claude Code uses for OTel — and re-indexes existing facts via the existing `IndexCommand` (skip with `--no-reindex`). Supports `--status` for current config and `--dry-run` for inspection. Preserves unrelated settings.json keys.
|
|
42
|
+
- **`Checks::EmbeddingsCheck`** in `claude-memory doctor` — surfaces the active embedding provider name and dimensions, hints to set `CLAUDE_MEMORY_EMBEDDING_PROVIDER=fastembed` when on tfidf default and fastembed is loadable, and reports dimension mismatches between stored vectors and the current provider. Closes the visibility gap where a user could see `sqlite-vec available ✓` while silently running on tfidf without knowing.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **`plugin.json` declared `skills: "./skills/"` and `outputStyles: "./output-styles/"` pointing at non-existent directories.** Per Claude Code's plugin reference, `distill-transcripts.md` is correctly a flat *command* (not a skill); both forms register as `/<name>` slash commands. Dead keys removed. Plugin spec rewritten as deletion-safe ("every directory key in plugin.json points at an existing directory") so this can't regress.
|
|
47
|
+
|
|
48
|
+
### Documentation
|
|
49
|
+
|
|
50
|
+
- **README "Upgrading" section** now documents the marketplace-refresh + `/reload-plugins` flow explicitly. After `/plugin marketplace update <name>` users must run `/reload-plugins` or restart Claude Code for new slash commands to appear — this bit one user upgrading to 0.12.0 looking for `/distill-transcripts`. Includes `/audit-memory` and `/distill-transcripts` as named examples.
|
|
51
|
+
|
|
52
|
+
### Upgrade Notes
|
|
53
|
+
|
|
54
|
+
- No DB migrations. Schema stays at v18.
|
|
55
|
+
- After `gem update claude_memory`, run `/plugin marketplace update claude-memory && /reload-plugins` (or restart Claude Code) to see the new `/distill-transcripts` and `/audit-memory` slash commands.
|
|
56
|
+
- Existing fact bases continue to use whatever embedding provider they were indexed under. To opt into fastembed, run `claude-memory setup-vectors` — it handles provider switching + re-index in one step.
|
|
57
|
+
- `claude-memory doctor` will now emit a warning on tfidf default with fastembed loadable. This is informational, not an error; the system continues to function on tfidf.
|
|
58
|
+
|
|
7
59
|
## [0.12.0] - 2026-05-29
|
|
8
60
|
|
|
9
61
|
Theme: **Release Discipline + Observability + Self-Audit** — the infrastructure that makes a 1.0 semver promise defensible. This release locks down the public API surface, adds the observability primitives (OTel ingestion, dashboard Telemetry) and the self-audit toolkit (`claude-memory audit`) that serve the visibility pillar, and ships the negative-fact harm benchmark + staleness guard that make the long-horizon-quality claim measurable rather than aspirational.
|
data/CLAUDE.md
CHANGED
|
@@ -113,7 +113,7 @@ bin/run-evals --comparative --setup-competitors # Install + run in one step
|
|
|
113
113
|
|
|
114
114
|
NullDistiller (regex, Layer 1):
|
|
115
115
|
- Concept Recall: 0.952 (regex-detectable entities/facts)
|
|
116
|
-
- Fact Precision:
|
|
116
|
+
- Fact Precision: 0.935, Fact Recall: 1.000 (on 31 test cases) — deliberately high-recall ("extract every mention, filter downstream"); see improvements.md #70
|
|
117
117
|
- Pipeline latency: P95 < 5ms (medium text)
|
|
118
118
|
|
|
119
119
|
Claude Code (LLM, Layers 2+3):
|
|
@@ -150,7 +150,7 @@ Transcripts → Ingest → Index (FTS5)
|
|
|
150
150
|
The distillation pipeline operates at three levels of depth:
|
|
151
151
|
|
|
152
152
|
- **Layer 1: NullDistiller** (automatic, regex, free) — Runs in the ingest pipeline on every hook event. Extracts entities, facts, and scope hints using pattern matching. P95 latency < 5ms.
|
|
153
|
-
- **Layer 2: Context Hook Injection** (automatic, LLM, zero extra cost) — At SessionStart, undistilled content is injected into the session via `hookSpecificOutput.additionalContext` with extraction instructions. Claude Code itself acts as the distiller, extracting structured facts at no additional API cost.
|
|
153
|
+
- **Layer 2: Context Hook Injection** (automatic, LLM, zero extra cost) — At SessionStart, undistilled content is injected into the session via `hookSpecificOutput.additionalContext` with extraction instructions. Claude Code itself acts as the distiller, extracting structured facts at no additional API cost. The same prompt also asks Claude to emit episodic **observations** (the Layer-2 Claude-as-observer) in the `observations` field of its `memory.store_extraction` call — coerced/validated at the handler border and persisted via the resolver alongside facts.
|
|
154
154
|
- **Layer 3: `/distill-transcripts` Skill** (manual, on-demand) — Deep extraction triggered by the user. Processes undistilled content with depth-aware prompts (initial extraction, consolidation, contradiction resolution).
|
|
155
155
|
|
|
156
156
|
New MCP tools `memory.undistilled` and `memory.mark_distilled` support the pipeline by tracking which content items have been deeply distilled.
|
|
@@ -167,7 +167,7 @@ New MCP tools `memory.undistilled` and `memory.mark_distilled` support the pipel
|
|
|
167
167
|
- Each command is a separate class (HelpCommand, DoctorCommand, etc.)
|
|
168
168
|
- All commands inherit from BaseCommand
|
|
169
169
|
- Dependency injection for I/O (stdout, stderr, stdin)
|
|
170
|
-
-
|
|
170
|
+
- 38 commands total, each focused on single responsibility
|
|
171
171
|
|
|
172
172
|
- **`Configuration`**: Centralized ENV access (`configuration.rb`)
|
|
173
173
|
- Single source of truth for paths and environment variables
|
|
@@ -233,7 +233,7 @@ New MCP tools `memory.undistilled` and `memory.mark_distilled` support the pipel
|
|
|
233
233
|
- Modes: shared (repo), local (uncommitted), home (user directory)
|
|
234
234
|
|
|
235
235
|
- **`MCP`**: Model Context Protocol server and tools (`mcp/`)
|
|
236
|
-
- Exposes memory tools to Claude Code (
|
|
236
|
+
- Exposes memory tools to Claude Code (28 tools total)
|
|
237
237
|
- `Telemetry`: Records tool invocations to `mcp_tool_calls` table for usage stats
|
|
238
238
|
- Dual content/structuredContent responses with compact mode
|
|
239
239
|
|
|
@@ -256,6 +256,7 @@ Key tables (defined in `sqlite_store.rb`):
|
|
|
256
256
|
- `mcp_tool_calls`: MCP server tool invocation telemetry (schema v13)
|
|
257
257
|
- `activity_events`: Hook/recall/context/sweep/nudge telemetry (schema v15) — powers the dashboard timeline, moments feed, efficacy reports. Event types: `hook_ingest`, `hook_context` (carries `context_tokens` since 0.11.0), `hook_sweep`, `hook_publish`, `recall`, `store_extraction`, `roi_nudge` (since 0.11.0).
|
|
258
258
|
- `moment_feedback`: Per-moment 👍/👎 verdicts with optional notes (schema v16) — unique on event_id, repeat clicks upsert
|
|
259
|
+
- `observations`: Episodic "what happened" layer (schema v19–v20) — append-only narrative rows complementing facts ("what is true"). Columns: `body`, `kind` (decision/preference/event/…), `priority` (1=🔴/2=🟡/3=info), `scope`, `source_content_item_id` (provenance), `consolidated_into` (Reflector tombstone lineage — never hard-deleted), `token_count`, `status`, `corroboration_count` (folded by dedup; the promotion-gate signal), `promoted_at`/`promoted_fact_id` (set when promoted to a fact). Written by the Resolver from `Extraction#observations` (NullDistiller is the Layer-1 Observer). The full observational layer (Observer → injection → deterministic Reflector → promotion bridge) is in `lib/claude_memory/observe/`; see [docs/influence/mastra-observational-memory.md](docs/influence/mastra-observational-memory.md).
|
|
259
260
|
|
|
260
261
|
Facts include:
|
|
261
262
|
- `scope`: "global" or "project" (determines applicability)
|
|
@@ -339,7 +340,7 @@ Also update `SECTION_MAP` if the predicate should appear in a specific snapshot
|
|
|
339
340
|
|
|
340
341
|
- `lib/claude_memory.rb`: Main module, requires, database path helpers
|
|
341
342
|
- `lib/claude_memory/cli.rb`: Thin command router (41 lines)
|
|
342
|
-
- `lib/claude_memory/commands/`: Individual command classes (
|
|
343
|
+
- `lib/claude_memory/commands/`: Individual command classes (38 commands)
|
|
343
344
|
- `lib/claude_memory/configuration.rb`: Centralized configuration and ENV access
|
|
344
345
|
- `lib/claude_memory/domain/`: Domain models (Fact, Entity, Provenance, Conflict)
|
|
345
346
|
- `lib/claude_memory/core/`: Value objects and null objects
|
|
@@ -354,7 +355,7 @@ Also update `SECTION_MAP` if the predicate should appear in a specific snapshot
|
|
|
354
355
|
|
|
355
356
|
The gem includes an MCP server (`claude-memory serve-mcp`) that exposes memory operations as tools. Configuration should be in `.mcp.json` at project root.
|
|
356
357
|
|
|
357
|
-
Available MCP tools (
|
|
358
|
+
Available MCP tools (28 total):
|
|
358
359
|
- **Query & Recall**: `memory.recall`, `memory.recall_index`, `memory.recall_details`, `memory.recall_semantic`, `memory.search_concepts`
|
|
359
360
|
- **Provenance**: `memory.explain`, `memory.fact_graph`
|
|
360
361
|
- **Shortcuts**: `memory.decisions`, `memory.conventions`, `memory.architecture`
|
|
@@ -362,6 +363,7 @@ Available MCP tools (23 total):
|
|
|
362
363
|
- **Management**: `memory.promote`, `memory.reject_fact`, `memory.store_extraction`
|
|
363
364
|
- **Distillation**: `memory.undistilled`, `memory.mark_distilled`
|
|
364
365
|
- **Monitoring**: `memory.status`, `memory.stats`, `memory.changes`, `memory.conflicts`, `memory.activity`
|
|
366
|
+
- **Observational layer** (experimental): `memory.observations` (read-only episodic log), `memory.promote_observation` (corroboration-gated observation→fact promotion), `memory.consolidate_observations` (semantic reflection: merge related observations, combine corroboration)
|
|
365
367
|
- **Maintenance**: `memory.sweep_now`
|
|
366
368
|
- **Discovery**: `memory.check_setup`, `memory.list_projects`
|
|
367
369
|
|
|
@@ -373,13 +375,16 @@ ClaudeMemory integrates with Claude Code via hooks in `.claude/settings.json`:
|
|
|
373
375
|
- Calls `claude-memory hook ingest` with stdin JSON
|
|
374
376
|
- Reads transcript delta and updates both global and project databases
|
|
375
377
|
|
|
376
|
-
- **Context hook**: Triggers on SessionStart
|
|
378
|
+
- **Context hook**: Triggers on SessionStart (and PreCompact — see below)
|
|
377
379
|
- Calls `claude-memory hook context`
|
|
378
380
|
- Injects recent facts via `hookSpecificOutput.additionalContext`
|
|
381
|
+
- Two-block layout (observational layer): Block 1 = the episodic observation log (`Observe::ObservationsRenderer`, 🔴-marked), Block 2 = the undistilled "Pending Knowledge Extraction" tail. `ContextInjector#emitted_observation_count` feeds the `hook_context` telemetry.
|
|
382
|
+
- On **PreCompact** the same `claude-memory hook context` injects only the reflection nudge (`ContextInjector#reflection_context` — the promote/consolidate instructions for corroborated/related observations), not the full snapshot, since PreCompact is context-pressure (Mastra's token-threshold analog). `HooksConfigurator` wires it into the PreCompact hook set alongside ingest + sweep.
|
|
379
383
|
|
|
380
384
|
- **Sweep hook**: Triggers on PreCompact/SessionEnd events
|
|
381
385
|
- Runs time-bounded maintenance on both databases
|
|
382
386
|
- Cleans up vec0 entries for superseded/expired facts
|
|
387
|
+
- Runs the deterministic observation Reflector (`Observe::Reflector` via `Maintenance#reflect_observations`): dedupes near-identical observations + expires stale 🟢 info-level ones (TTL `observation_info_ttl_days`). Free/no-LLM, provenance-preserving (tombstone). Context-pressure-triggered — the analog of Mastra's token-threshold reflection.
|
|
383
388
|
|
|
384
389
|
- **Nudge hook** (0.11.0+): Triggers on SessionEnd, fires after ingest+sweep
|
|
385
390
|
- Calls `claude-memory hook nudge`
|
|
@@ -394,7 +399,7 @@ Hook commands read JSON payloads from stdin for robustness. Supports `--async` f
|
|
|
394
399
|
|
|
395
400
|
Local web UI for inspecting memory state. Started via `claude-memory dashboard` (default port 3377). Reads from both global and project databases; no write side effects from page loads.
|
|
396
401
|
|
|
397
|
-
The dashboard is a thin web layer over the same `Recall`/`Conflicts`/`Trust`/`Moments`/`Knowledge`/`Reuse`/`Health`/`Timeline` classes the MCP server uses. Each panel is backed by a dedicated module under `lib/claude_memory/dashboard/`; `Dashboard::API` holds HTTP-shape glue and per-endpoint formatting (delegating non-trivial logic to the panel classes).
|
|
402
|
+
The dashboard is a thin web layer over the same `Recall`/`Conflicts`/`Trust`/`Moments`/`Knowledge`/`Reuse`/`Health`/`Timeline`/`Observations` classes the MCP server uses. Each panel is backed by a dedicated module under `lib/claude_memory/dashboard/`; `Dashboard::API` holds HTTP-shape glue and per-endpoint formatting (delegating non-trivial logic to the panel classes). The `Observations` panel (`/api/observations`, Advanced → Observations tab) surfaces the episodic layer: counts by status/kind/priority, corroboration + promotion readiness, a compression ratio (source content tokens ÷ observation tokens), and a recent timeline.
|
|
398
403
|
|
|
399
404
|
Connections are released after each request — never holds a WAL writer lock open across page loads.
|
|
400
405
|
|
data/README.md
CHANGED
|
@@ -12,9 +12,13 @@ It automatically:
|
|
|
12
12
|
- ✅ Remembers project-specific and global knowledge
|
|
13
13
|
- ✅ Provides instant recall without manual prompting
|
|
14
14
|
- ✅ Maintains truth (handles conflicts, supersession)
|
|
15
|
+
- ✅ Tracks *what happened*, not just *what's true* — an episodic observation log alongside the facts (0.13.0+)
|
|
16
|
+
- ✅ Promotes an observation to a fact only after it recurs — a corroboration gate against one-off noise
|
|
15
17
|
|
|
16
18
|
**No API keys. No configuration. Just works.**
|
|
17
19
|
|
|
20
|
+
ClaudeMemory now has **two complementary halves**: a *semantic* fact store ("what is true" — your stack, conventions, decisions) and an *episodic* observation layer ("what happened" — the narrative of your sessions). Observations are deduplicated and consolidated automatically, and only graduate to facts once corroborated — so fleeting mentions never harden into false memory. See [Episodic Memory](#episodic-memory-observations).
|
|
21
|
+
|
|
18
22
|
## Quick Start
|
|
19
23
|
|
|
20
24
|
### 1. Install the Gem
|
|
@@ -137,6 +141,7 @@ File-searchable questions ("what version is this?") and one-shot code generation
|
|
|
137
141
|
- **Progressive Disclosure**: Lightweight queries before full details
|
|
138
142
|
- **Semantic Shortcuts**: Quick access to decisions, conventions, architecture
|
|
139
143
|
- **Truth Maintenance**: Automatic conflict resolution
|
|
144
|
+
- **Episodic Memory** (0.13.0+): An append-only observation log of *what happened* alongside the semantic fact store. Auto-consolidated via deterministic + LLM reflection on `PreCompact`/`SessionEnd`; corroborated observations are promoted to facts (anti-hallucination gate). See **[Episodic Memory →](#episodic-memory-observations)**.
|
|
140
145
|
- **Claude-Powered**: Uses Claude's intelligence to extract facts (no API key needed)
|
|
141
146
|
- **Token Efficient**: 10x reduction in memory queries with progressive disclosure
|
|
142
147
|
- **Database Maintenance**: Compact, export, and backup commands
|
|
@@ -152,6 +157,36 @@ File-searchable questions ("what version is this?") and one-shot code generation
|
|
|
152
157
|
|
|
153
158
|
Only metrics and event names are captured by default — verbatim prompts and bodies stay off until you explicitly opt in via `claude-memory otel --capture-prompts`. The receiver binds to `127.0.0.1` only.
|
|
154
159
|
|
|
160
|
+
## Episodic Memory (Observations)
|
|
161
|
+
|
|
162
|
+
Facts answer **"what is true"** (your stack, conventions, decisions). Observations answer **"what happened"** — a narrative log of the moments in your sessions. ClaudeMemory now keeps both, modeled on [Mastra's Observational Memory](docs/influence/mastra-observational-memory.md).
|
|
163
|
+
|
|
164
|
+
| | Facts (semantic) | Observations (episodic) |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| Capture | Durable truths — `uses_database: sqlite` | Narrative events — "decided to add a corroboration gate to avoid reject-churn" |
|
|
167
|
+
| Change | Explicitly, via supersession/rejection | Automatically — deduped, consolidated, low-priority ones expire |
|
|
168
|
+
| Promotion | — | Promoted to a fact only after corroboration (≥2 sightings) |
|
|
169
|
+
|
|
170
|
+
**Why it's a leap forward:** the distiller used to commit a fact the first time it saw a claim — so a database mentioned once in a comparison could harden into a false `uses_database`. The observation layer makes repeated sighting the gate: an observation becomes a fact only after it recurs. That's an **anti-hallucination defense built into the memory model**, not a cleanup afterthought.
|
|
171
|
+
|
|
172
|
+
**How it runs (no extra API cost):**
|
|
173
|
+
- **Observer** — a regex Layer-1 pass plus Claude-as-observer in the SessionStart context hook emit observations as sessions happen.
|
|
174
|
+
- **Reflector** — deterministic dedup + TTL-expiry runs on `PreCompact`/`SessionEnd`; semantic consolidation rides the next turn's context hook (Claude-as-reflector). Superseded observations are *tombstoned*, never deleted, preserving provenance.
|
|
175
|
+
- **Promotion bridge** — corroborated observations graduate to facts on the corroboration gate.
|
|
176
|
+
|
|
177
|
+
**See it / use it:** the dashboard's **Observations** panel (counts by kind/priority, corroboration + promotion readiness, source→observation compression ratio, recent timeline); the `claude-memory observations` CLI; the `memory.observations` / `memory.promote_observation` / `memory.consolidate_observations` MCP tools; and the `/reflect` skill for a guided survey→consolidate→promote pass.
|
|
178
|
+
|
|
179
|
+
## What's New in 0.13.0
|
|
180
|
+
|
|
181
|
+
**Episodic Observation Layer** — ClaudeMemory gains a second kind of memory (see [Episodic Memory](#episodic-memory-observations) above):
|
|
182
|
+
|
|
183
|
+
- New `observations` table (schema v19–v20), append-only with `consolidated_into` tombstone lineage and `corroboration_count` / `promoted_at` / `promoted_fact_id` promotion tracking.
|
|
184
|
+
- Two-block SessionStart injection: a stable observation log (🔴-marked) + the undistilled "pending knowledge" tail.
|
|
185
|
+
- Automatic reflection on `PreCompact` (context-pressure, Mastra's token-threshold analog) and `SessionEnd` — deterministic GC shell-side in Ruby, semantic consolidation via the context hook (no extra API spend).
|
|
186
|
+
- Corroboration-gated observation→fact promotion — repeated sightings required before commitment, an anti-hallucination gate against reject-churn from one-off doc/example text.
|
|
187
|
+
- New surfaces: dashboard **Observations** panel, `claude-memory observations` command (+ `claude-memory stats --observations`), `claude-memory audit` observation health checks, three `memory.*observation*` MCP tools, and the `/reflect` skill.
|
|
188
|
+
- Dependencies refreshed to current (sequel, standard, rubocop, and others).
|
|
189
|
+
|
|
155
190
|
## What's New in 0.11.0
|
|
156
191
|
|
|
157
192
|
Five user-visible signals so you can answer "is memory still worth it?" with
|
|
@@ -238,6 +273,17 @@ gem update claude_memory
|
|
|
238
273
|
|
|
239
274
|
All database migrations happen automatically. Run `claude-memory doctor` to verify.
|
|
240
275
|
|
|
276
|
+
### After upgrading: refresh the Claude Code plugin
|
|
277
|
+
|
|
278
|
+
If you installed claude-memory as a Claude Code plugin (via the marketplace), pull the latest plugin spec **and reload it in your current session**:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
/plugin marketplace update claude-memory
|
|
282
|
+
/reload-plugins
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`/plugin marketplace update` refreshes the plugin manifest from the source, but **the new slash commands won't appear until you run `/reload-plugins` (or restart Claude Code)** — slash commands are loaded once at session start. This bites every release that adds a new command (e.g. `/distill-transcripts` in 0.11, `/audit-memory` in 0.12); if a documented slash command doesn't autocomplete after upgrade, `/reload-plugins` is the fix.
|
|
286
|
+
|
|
241
287
|
See [CHANGELOG.md](CHANGELOG.md) for detailed release notes.
|
|
242
288
|
|
|
243
289
|
## Troubleshooting
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration v19: Add observations table — the episodic memory layer.
|
|
4
|
+
#
|
|
5
|
+
# Facts answer "what is true" (semantic memory); observations answer "what
|
|
6
|
+
# happened" (episodic memory). This is the storage half of Phase 1 of the
|
|
7
|
+
# observational layer (see docs/influence/mastra-observational-memory.md).
|
|
8
|
+
#
|
|
9
|
+
# Observations are append-only: the Reflector consolidates by writing a new
|
|
10
|
+
# observation and pointing superseded ones at it via consolidated_into,
|
|
11
|
+
# rather than hard-deleting — preserving provenance (unlike Mastra's lossy
|
|
12
|
+
# drop). source_content_item_id links each observation back to the raw
|
|
13
|
+
# transcript chunk it was distilled from.
|
|
14
|
+
Sequel.migration do
|
|
15
|
+
up do
|
|
16
|
+
create_table?(:observations) do
|
|
17
|
+
primary_key :id
|
|
18
|
+
String :body, text: true, null: false # dense narrative text — the observation itself
|
|
19
|
+
String :kind, null: false, default: "event" # user_statement | agent_action | tool_result | preference | decision | event
|
|
20
|
+
Integer :priority, null: false, default: 3 # 1=important (🔴), 2=maybe (🟡), 3=info (🟢)
|
|
21
|
+
String :scope, null: false, default: "project" # "project" or "global"
|
|
22
|
+
String :project_path # set for project-scoped observations
|
|
23
|
+
Integer :source_content_item_id # provenance: raw transcript chunk
|
|
24
|
+
Integer :consolidated_into # Reflector lineage: id of the observation this was merged into
|
|
25
|
+
Integer :token_count # for budget / compression math (Phase 2)
|
|
26
|
+
String :status, null: false, default: "active" # "active" or "consolidated"
|
|
27
|
+
String :session_id # session that produced the observation
|
|
28
|
+
String :observed_at, null: false # ISO 8601 event time
|
|
29
|
+
String :created_at, null: false # ISO 8601 row creation time
|
|
30
|
+
String :reflected_at # ISO 8601 — set when the Reflector last touched it
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_status ON observations(status)"
|
|
34
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_scope ON observations(scope)"
|
|
35
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_observed_at ON observations(observed_at)"
|
|
36
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_source ON observations(source_content_item_id)"
|
|
37
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_consolidated_into ON observations(consolidated_into)"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
down do
|
|
41
|
+
drop_table?(:observations)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration v20: observation→fact promotion bridge (Phase 4 of the
|
|
4
|
+
# observational layer).
|
|
5
|
+
#
|
|
6
|
+
# - corroboration_count: how many times this observation has been sighted.
|
|
7
|
+
# Starts at 1; the deterministic Reflector's dedup pass folds duplicates'
|
|
8
|
+
# counts into the keeper instead of just dropping them. This count is the
|
|
9
|
+
# "repeated sightings" signal the promotion gate requires — an observation
|
|
10
|
+
# is only eligible to become a structured fact once corroborated, which
|
|
11
|
+
# doubles as an anti-hallucination gate against one-off doc/example text.
|
|
12
|
+
# - promoted_at / promoted_fact_id: set when an observation has been promoted
|
|
13
|
+
# to a fact, so it is not re-suggested. The observation row is preserved
|
|
14
|
+
# (provenance), it just stops appearing as a promotion candidate.
|
|
15
|
+
Sequel.migration do
|
|
16
|
+
up do
|
|
17
|
+
alter_table(:observations) do
|
|
18
|
+
add_column :corroboration_count, Integer, null: false, default: 1
|
|
19
|
+
add_column :promoted_at, String # ISO 8601, set on promotion
|
|
20
|
+
add_column :promoted_fact_id, Integer # the fact this was promoted into
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
run "CREATE INDEX IF NOT EXISTS idx_observations_promoted_at ON observations(promoted_at)"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
down do
|
|
27
|
+
alter_table(:observations) do
|
|
28
|
+
drop_column :corroboration_count
|
|
29
|
+
drop_column :promoted_at
|
|
30
|
+
drop_column :promoted_fact_id
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|