claude_memory 0.3.0 → 0.5.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/CLAUDE.md +1 -1
- data/.claude/output-styles/memory-aware.md +1 -0
- data/.claude/rules/claude_memory.generated.md +9 -34
- data/.claude/settings.local.json +4 -1
- data/.claude/skills/check-memory/DEPRECATED.md +29 -0
- data/.claude/skills/check-memory/SKILL.md +10 -0
- data/.claude/skills/debug-memory +1 -0
- data/.claude/skills/improve/SKILL.md +12 -1
- data/.claude/skills/memory-first-workflow +1 -0
- data/.claude/skills/setup-memory +1 -0
- data/.claude-plugin/plugin.json +1 -1
- data/.lefthook/map_specs.rb +29 -0
- data/CHANGELOG.md +83 -5
- data/CLAUDE.md +38 -0
- data/README.md +43 -0
- data/Rakefile +14 -1
- data/WEEK2_COMPLETE.md +250 -0
- data/db/migrations/008_add_provenance_line_range.rb +21 -0
- data/db/migrations/009_add_docid.rb +39 -0
- data/db/migrations/010_add_llm_cache.rb +30 -0
- data/docs/architecture.md +49 -14
- data/docs/ci_integration.md +294 -0
- data/docs/eval_week1_summary.md +183 -0
- data/docs/eval_week2_summary.md +419 -0
- data/docs/evals.md +353 -0
- data/docs/improvements.md +72 -1085
- data/docs/influence/claude-supermemory.md +498 -0
- data/docs/influence/qmd.md +424 -2022
- data/docs/quality_review.md +64 -705
- data/lefthook.yml +8 -1
- data/lib/claude_memory/commands/doctor_command.rb +45 -4
- data/lib/claude_memory/commands/explain_command.rb +11 -6
- data/lib/claude_memory/commands/stats_command.rb +1 -1
- data/lib/claude_memory/core/fact_graph.rb +122 -0
- data/lib/claude_memory/core/fact_query_builder.rb +34 -14
- data/lib/claude_memory/core/fact_ranker.rb +3 -20
- data/lib/claude_memory/core/relative_time.rb +45 -0
- data/lib/claude_memory/core/result_sorter.rb +2 -2
- data/lib/claude_memory/core/rr_fusion.rb +57 -0
- data/lib/claude_memory/core/snippet_extractor.rb +97 -0
- data/lib/claude_memory/domain/fact.rb +3 -1
- data/lib/claude_memory/embeddings/fastembed_adapter.rb +55 -0
- data/lib/claude_memory/index/index_query.rb +2 -0
- data/lib/claude_memory/index/lexical_fts.rb +18 -0
- data/lib/claude_memory/infrastructure/operation_tracker.rb +7 -21
- data/lib/claude_memory/infrastructure/schema_validator.rb +30 -25
- data/lib/claude_memory/ingest/content_sanitizer.rb +8 -1
- data/lib/claude_memory/ingest/ingester.rb +74 -59
- data/lib/claude_memory/ingest/tool_extractor.rb +1 -1
- data/lib/claude_memory/ingest/tool_filter.rb +55 -0
- data/lib/claude_memory/logging/logger.rb +112 -0
- data/lib/claude_memory/mcp/query_guide.rb +96 -0
- data/lib/claude_memory/mcp/response_formatter.rb +86 -23
- data/lib/claude_memory/mcp/server.rb +34 -4
- data/lib/claude_memory/mcp/text_summary.rb +257 -0
- data/lib/claude_memory/mcp/tool_definitions.rb +27 -11
- data/lib/claude_memory/mcp/tools.rb +133 -120
- data/lib/claude_memory/publish.rb +12 -2
- data/lib/claude_memory/recall/expansion_detector.rb +44 -0
- data/lib/claude_memory/recall.rb +93 -41
- data/lib/claude_memory/resolve/resolver.rb +72 -40
- data/lib/claude_memory/store/sqlite_store.rb +99 -24
- data/lib/claude_memory/sweep/sweeper.rb +6 -0
- data/lib/claude_memory/version.rb +1 -1
- data/lib/claude_memory.rb +21 -0
- data/output-styles/memory-aware.md +71 -0
- data/skills/debug-memory/SKILL.md +146 -0
- data/skills/memory-first-workflow/SKILL.md +144 -0
- metadata +29 -5
- data/.claude/.mind.mv2.o2N83S +0 -0
- data/.claude/output-styles/memory-aware.md +0 -21
- data/docs/.claude/mind.mv2.lock +0 -0
- data/docs/remaining_improvements.md +0 -330
- /data/{.claude/skills → skills}/setup-memory/SKILL.md +0 -0
data/WEEK2_COMPLETE.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Week 2 Complete! 🎉
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
**Week 2: Extract Patterns** - ✅ Complete
|
|
6
|
+
|
|
7
|
+
After implementing 3 eval scenarios in Week 1, clear patterns emerged. Week 2 extracted these patterns into reusable helpers, making it faster and easier to add new eval scenarios.
|
|
8
|
+
|
|
9
|
+
## What We Accomplished
|
|
10
|
+
|
|
11
|
+
### 1. Created Helper Modules (`spec/evals/support/eval_helpers.rb`)
|
|
12
|
+
|
|
13
|
+
**145 lines of reusable code:**
|
|
14
|
+
|
|
15
|
+
- **SharedSetup**: Common RSpec setup (tmpdir, db_path, cleanup)
|
|
16
|
+
- **MemoryFixtureBuilder**: Declarative memory population
|
|
17
|
+
- **ResponseStubs**: Standardized stub responses
|
|
18
|
+
- **ScoringHelpers**: Common scoring utilities
|
|
19
|
+
|
|
20
|
+
### 2. Refactored All 3 Evals
|
|
21
|
+
|
|
22
|
+
**Before** (Week 1 - Inline everything):
|
|
23
|
+
```ruby
|
|
24
|
+
def populate_fixture_memory
|
|
25
|
+
store = ClaudeMemory::Store::SQLiteStore.new(db_path)
|
|
26
|
+
entity_id = store.find_or_create_entity(type: "repo", name: "test-project")
|
|
27
|
+
|
|
28
|
+
fact_id_1 = store.insert_fact(...)
|
|
29
|
+
content_id_1 = store.upsert_content_item(...)
|
|
30
|
+
store.insert_provenance(...)
|
|
31
|
+
fts = ClaudeMemory::Index::LexicalFTS.new(store)
|
|
32
|
+
fts.index_content_item(...)
|
|
33
|
+
# ... repeat for more facts
|
|
34
|
+
|
|
35
|
+
store.close
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**After** (Week 2 - Declarative with helpers):
|
|
40
|
+
```ruby
|
|
41
|
+
def populate_fixture_memory
|
|
42
|
+
builder = EvalHelpers::MemoryFixtureBuilder.new(db_path)
|
|
43
|
+
|
|
44
|
+
builder.add_facts([
|
|
45
|
+
{
|
|
46
|
+
predicate: "convention",
|
|
47
|
+
object: "Use 2-space indentation",
|
|
48
|
+
text: "Use 2-space indentation for Ruby files",
|
|
49
|
+
fts_keywords: "coding convention style"
|
|
50
|
+
}
|
|
51
|
+
])
|
|
52
|
+
|
|
53
|
+
builder.close
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Improvements**:
|
|
58
|
+
- ✅ Clearer intent (what, not how)
|
|
59
|
+
- ✅ Less duplication (DRY)
|
|
60
|
+
- ✅ Easier to maintain (single place to fix bugs)
|
|
61
|
+
- ✅ Faster to add new evals (~30 min vs 1 hour)
|
|
62
|
+
|
|
63
|
+
### 3. Maintained 100% Test Pass Rate
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
============================================================
|
|
67
|
+
EVAL SUMMARY
|
|
68
|
+
============================================================
|
|
69
|
+
|
|
70
|
+
Total Examples: 15
|
|
71
|
+
Passed: 15 ✅
|
|
72
|
+
Failed: 0 ❌
|
|
73
|
+
Duration: 0.23s
|
|
74
|
+
|
|
75
|
+
============================================================
|
|
76
|
+
BEHAVIORAL SCORES
|
|
77
|
+
============================================================
|
|
78
|
+
|
|
79
|
+
Convention Recall: +100% improvement
|
|
80
|
+
Architectural Decision: +100% improvement
|
|
81
|
+
Tech Stack Recall: +100% improvement
|
|
82
|
+
|
|
83
|
+
OVERALL: Memory improves responses by 100% on average
|
|
84
|
+
============================================================
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Test Results
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
$ bundle exec rspec spec/evals/
|
|
91
|
+
|
|
92
|
+
Architectural Decision Eval
|
|
93
|
+
✓ calculates behavioral score for decision adherence
|
|
94
|
+
✓ mentions the stored architectural decision
|
|
95
|
+
✓ has lower decision adherence score
|
|
96
|
+
✓ gives generic advice without knowing the decision
|
|
97
|
+
✓ creates memory database with architectural decision
|
|
98
|
+
|
|
99
|
+
Convention Recall Eval
|
|
100
|
+
✓ mentions stored conventions when asked
|
|
101
|
+
✓ calculates behavioral score
|
|
102
|
+
✓ does not mention specific project conventions
|
|
103
|
+
✓ has lower behavioral score than memory-enabled
|
|
104
|
+
✓ creates memory database with conventions
|
|
105
|
+
|
|
106
|
+
Tech Stack Recall Eval
|
|
107
|
+
✓ has lower accuracy score
|
|
108
|
+
✓ cannot identify the specific framework without memory
|
|
109
|
+
✓ correctly identifies the testing framework
|
|
110
|
+
✓ calculates accuracy score
|
|
111
|
+
✓ creates memory database with tech stack facts
|
|
112
|
+
|
|
113
|
+
Finished in 0.20s
|
|
114
|
+
15 examples, 0 failures ✅
|
|
115
|
+
|
|
116
|
+
Full test suite: 1003 examples, 0 failures ✅
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Design Principles Followed
|
|
120
|
+
|
|
121
|
+
### Sandi Metz: Extract Only When Painful
|
|
122
|
+
> "Extract collaborators only when you feel pain"
|
|
123
|
+
|
|
124
|
+
- ✅ Week 1: Inline everything, no abstractions
|
|
125
|
+
- ✅ Week 2: Felt pain after 3 evals, extracted patterns
|
|
126
|
+
- ✅ Right timing: Based on real needs, not speculation
|
|
127
|
+
|
|
128
|
+
### Kent Beck: Incremental Design
|
|
129
|
+
> "Make it work, make it right, make it fast"
|
|
130
|
+
|
|
131
|
+
- ✅ Week 1: Make it work (3 evals passing)
|
|
132
|
+
- ✅ Week 2: Make it right (extract patterns)
|
|
133
|
+
- ⏸️ Week 3: Make it fast (if needed)
|
|
134
|
+
|
|
135
|
+
### Avdi Grimm: Tell, Don't Ask
|
|
136
|
+
- ✅ Before: Imperative (tell store.insert_fact, then insert_provenance, then...)
|
|
137
|
+
- ✅ After: Declarative (tell builder.add_fact with all details)
|
|
138
|
+
|
|
139
|
+
## Files Modified
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
spec/evals/support/
|
|
143
|
+
└── eval_helpers.rb # NEW: 145 lines
|
|
144
|
+
|
|
145
|
+
spec/evals/
|
|
146
|
+
├── convention_recall_spec.rb # REFACTORED
|
|
147
|
+
├── architectural_decision_spec.rb # REFACTORED
|
|
148
|
+
└── tech_stack_recall_spec.rb # REFACTORED
|
|
149
|
+
|
|
150
|
+
docs/
|
|
151
|
+
└── eval_week2_summary.md # NEW: Detailed summary
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Metrics
|
|
155
|
+
|
|
156
|
+
- **Lines added**: 145 (helpers)
|
|
157
|
+
- **Lines removed**: ~21 (duplication)
|
|
158
|
+
- **Net**: +124 lines, but much clearer intent
|
|
159
|
+
- **Time to add 4th eval**: ~30 min (was 1 hour)
|
|
160
|
+
- **Test pass rate**: 100% (15/15)
|
|
161
|
+
- **Full suite**: 1003 tests, all passing
|
|
162
|
+
|
|
163
|
+
## What's Next (Week 3+)
|
|
164
|
+
|
|
165
|
+
### Option A: Add More Scenarios ⭐ Recommended
|
|
166
|
+
**Why**: Helpers make this fast, more scenarios = more confidence
|
|
167
|
+
|
|
168
|
+
Potential scenarios:
|
|
169
|
+
- Implementation Consistency (follows existing patterns)
|
|
170
|
+
- Code Style Adherence (respects conventions)
|
|
171
|
+
- Framework Usage (uses correct APIs)
|
|
172
|
+
- Error Handling (applies project patterns)
|
|
173
|
+
|
|
174
|
+
**Time**: ~30 min per scenario
|
|
175
|
+
|
|
176
|
+
### Option B: Add Real Claude Execution
|
|
177
|
+
**Why**: Validate against actual Claude behavior
|
|
178
|
+
**Trade-offs**: Slow (30s+ per test), costs money, non-deterministic
|
|
179
|
+
|
|
180
|
+
### Option C: Tool Call Tracking
|
|
181
|
+
**Why**: Test whether memory tools are invoked (like Vercel's 56% skip rate)
|
|
182
|
+
**When**: If we need to test tool selection, not just outcomes
|
|
183
|
+
|
|
184
|
+
### Option D: Mode Comparison
|
|
185
|
+
**Why**: Compare MCP tools vs generated context vs both
|
|
186
|
+
**When**: If we want to validate dual-mode approach
|
|
187
|
+
|
|
188
|
+
## How to Use
|
|
189
|
+
|
|
190
|
+
### Run Evals
|
|
191
|
+
```bash
|
|
192
|
+
# Quick summary
|
|
193
|
+
./bin/run-evals
|
|
194
|
+
|
|
195
|
+
# Detailed output
|
|
196
|
+
bundle exec rspec spec/evals/ --format documentation
|
|
197
|
+
|
|
198
|
+
# Specific scenario
|
|
199
|
+
bundle exec rspec spec/evals/convention_recall_spec.rb
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Add New Scenario (With Helpers!)
|
|
203
|
+
```ruby
|
|
204
|
+
require_relative "support/eval_helpers"
|
|
205
|
+
|
|
206
|
+
RSpec.describe "Your New Eval", :eval do
|
|
207
|
+
include EvalHelpers::SharedSetup
|
|
208
|
+
include EvalHelpers::ResponseStubs
|
|
209
|
+
include EvalHelpers::ScoringHelpers
|
|
210
|
+
|
|
211
|
+
def populate_fixture_memory
|
|
212
|
+
builder = EvalHelpers::MemoryFixtureBuilder.new(db_path)
|
|
213
|
+
builder.add_fact(...)
|
|
214
|
+
builder.close
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# ... rest of eval
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Time to implement**: ~30 minutes 🚀
|
|
222
|
+
|
|
223
|
+
## Documentation
|
|
224
|
+
|
|
225
|
+
- `spec/evals/README.md` - Quick reference (updated)
|
|
226
|
+
- `spec/evals/QUICKSTART.md` - Quick start guide
|
|
227
|
+
- `docs/evals.md` - Comprehensive documentation (updated)
|
|
228
|
+
- `docs/eval_week1_summary.md` - Week 1 summary
|
|
229
|
+
- `docs/eval_week2_summary.md` - Week 2 detailed summary
|
|
230
|
+
|
|
231
|
+
## Success Criteria (All Met ✅)
|
|
232
|
+
|
|
233
|
+
- ✅ Extracted helpers after clear repetition
|
|
234
|
+
- ✅ All 15 tests still passing
|
|
235
|
+
- ✅ Faster to add new evals (30 min vs 1 hour)
|
|
236
|
+
- ✅ Clearer, more maintainable code
|
|
237
|
+
- ✅ No premature abstractions
|
|
238
|
+
- ✅ Linter passing
|
|
239
|
+
- ✅ Full test suite passing (1003 tests)
|
|
240
|
+
|
|
241
|
+
## Ready for Week 3
|
|
242
|
+
|
|
243
|
+
With helpers in place, the eval framework is now:
|
|
244
|
+
- ✅ **Proven** (15 tests, 100% pass rate)
|
|
245
|
+
- ✅ **Maintainable** (extracted patterns)
|
|
246
|
+
- ✅ **Extensible** (easy to add scenarios)
|
|
247
|
+
- ✅ **Fast** (<1s, suitable for TDD)
|
|
248
|
+
- ✅ **Quantified** (100% improvement with memory)
|
|
249
|
+
|
|
250
|
+
**Recommendation**: Proceed with Option A (add more scenarios) or wait for user feedback.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration v8: Add line range references to provenance
|
|
4
|
+
# - Adds line_start and line_end columns for precise source linking
|
|
5
|
+
# - Enables fact verification by pointing to exact location in source content
|
|
6
|
+
# - 1-indexed line numbers matching standard editor conventions
|
|
7
|
+
Sequel.migration do
|
|
8
|
+
up do
|
|
9
|
+
alter_table(:provenance) do
|
|
10
|
+
add_column :line_start, Integer # 1-indexed start line in source content
|
|
11
|
+
add_column :line_end, Integer # 1-indexed end line in source content
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
down do
|
|
16
|
+
alter_table(:provenance) do
|
|
17
|
+
drop_column :line_start
|
|
18
|
+
drop_column :line_end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration v9: Add docid (short hash identifier) to facts
|
|
4
|
+
# - Adds docid column for user-friendly fact references (e.g., "abc123de")
|
|
5
|
+
# - Docids are 8-character hex strings derived from SHA256 of fact content
|
|
6
|
+
# - Enables cross-database references and better UX in CLI/MCP
|
|
7
|
+
# - Backfills existing facts with generated docids
|
|
8
|
+
require "digest"
|
|
9
|
+
|
|
10
|
+
Sequel.migration do
|
|
11
|
+
up do
|
|
12
|
+
alter_table(:facts) do
|
|
13
|
+
add_column :docid, String, size: 8
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
run "CREATE UNIQUE INDEX IF NOT EXISTS idx_facts_docid ON facts(docid)"
|
|
17
|
+
|
|
18
|
+
# Backfill existing facts with docids
|
|
19
|
+
self[:facts].each do |fact|
|
|
20
|
+
input = "#{fact[:subject_entity_id]}:#{fact[:predicate]}:#{fact[:object_literal]}:#{fact[:created_at]}"
|
|
21
|
+
docid = Digest::SHA256.hexdigest(input)[0, 8]
|
|
22
|
+
|
|
23
|
+
# Handle unlikely collisions by appending id
|
|
24
|
+
existing = self[:facts].where(docid: docid).exclude(id: fact[:id]).first
|
|
25
|
+
if existing
|
|
26
|
+
docid = Digest::SHA256.hexdigest("#{input}:#{fact[:id]}")[0, 8]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
self[:facts].where(id: fact[:id]).update(docid: docid)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
down do
|
|
34
|
+
run "DROP INDEX IF EXISTS idx_facts_docid"
|
|
35
|
+
alter_table(:facts) do
|
|
36
|
+
drop_column :docid
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration v10: Add LLM response cache for distillation cost reduction
|
|
4
|
+
# - Creates llm_cache table to cache API responses by content hash
|
|
5
|
+
# - Cache key: SHA256(operation + model + input)
|
|
6
|
+
# - Enables significant cost savings for repeated/similar distillation requests
|
|
7
|
+
# - Includes TTL support via created_at for future cache expiration
|
|
8
|
+
Sequel.migration do
|
|
9
|
+
up do
|
|
10
|
+
create_table?(:llm_cache) do
|
|
11
|
+
primary_key :id
|
|
12
|
+
String :cache_key, null: false, unique: true # SHA256 hex digest
|
|
13
|
+
String :operation, null: false # e.g., "distill", "extract"
|
|
14
|
+
String :model, null: false # e.g., "claude-sonnet-4-20250514"
|
|
15
|
+
String :input_hash, null: false # SHA256 of input content
|
|
16
|
+
String :result_json, text: true, null: false # Cached JSON response
|
|
17
|
+
Integer :input_tokens # Tokens in request
|
|
18
|
+
Integer :output_tokens # Tokens in response
|
|
19
|
+
String :created_at, null: false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
run "CREATE INDEX IF NOT EXISTS idx_llm_cache_key ON llm_cache(cache_key)"
|
|
23
|
+
run "CREATE INDEX IF NOT EXISTS idx_llm_cache_created_at ON llm_cache(created_at)"
|
|
24
|
+
run "CREATE INDEX IF NOT EXISTS idx_llm_cache_operation ON llm_cache(operation)"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
down do
|
|
28
|
+
drop_table?(:llm_cache)
|
|
29
|
+
end
|
|
30
|
+
end
|
data/docs/architecture.md
CHANGED
|
@@ -22,12 +22,13 @@ ClaudeMemory is architected using Domain-Driven Design (DDD) principles with cle
|
|
|
22
22
|
┌──────────────────────▼──────────────────────────────────────┐
|
|
23
23
|
│ Business Logic Layer │
|
|
24
24
|
│ Recall → Resolve → Distill → Ingest → Publish │
|
|
25
|
-
│ Sweep → MCP → Hook
|
|
25
|
+
│ Sweep → Embeddings → MCP → Hook │
|
|
26
26
|
└──────────────────────┬──────────────────────────────────────┘
|
|
27
27
|
│
|
|
28
28
|
┌──────────────────────▼──────────────────────────────────────┐
|
|
29
29
|
│ Infrastructure Layer │
|
|
30
|
-
│ Store (SQLite
|
|
30
|
+
│ Store (SQLite v6 + WAL) → FileSystem → Index (FTS5+Vector) │
|
|
31
|
+
│ Templates │
|
|
31
32
|
└─────────────────────────────────────────────────────────────┘
|
|
32
33
|
```
|
|
33
34
|
|
|
@@ -94,6 +95,9 @@ end
|
|
|
94
95
|
- **SessionId**: Type-safe session identifiers
|
|
95
96
|
- **TranscriptPath**: Type-safe file paths
|
|
96
97
|
- **FactId**: Type-safe positive integer IDs
|
|
98
|
+
- **TextBuilder**: Searchable text construction from entities/facts/decisions
|
|
99
|
+
- **ResultSorter**: Result ranking and sorting logic
|
|
100
|
+
- **FactQueryBuilder**: SQL query construction for fact retrieval
|
|
97
101
|
- All are immutable (frozen) and self-validating
|
|
98
102
|
|
|
99
103
|
#### Null Objects (`core/`)
|
|
@@ -115,13 +119,14 @@ end
|
|
|
115
119
|
|
|
116
120
|
**Components:**
|
|
117
121
|
|
|
118
|
-
#### Recall (`recall.rb`)
|
|
122
|
+
#### Recall (`recall.rb` + `recall/`)
|
|
119
123
|
- Queries facts from global and project databases
|
|
120
124
|
- **Optimization**: Batch queries to eliminate N+1 issues
|
|
121
125
|
- Before: 2N+1 queries for N facts
|
|
122
126
|
- After: 3 queries total (FTS + batch facts + batch receipts)
|
|
123
127
|
- Supports scope filtering (project, global, all)
|
|
124
128
|
- Returns facts with provenance receipts
|
|
129
|
+
- `DualQueryTemplate`: Query template handling for dual-database queries
|
|
125
130
|
|
|
126
131
|
#### Resolve (`resolve/`)
|
|
127
132
|
- Truth maintenance and conflict resolution
|
|
@@ -149,9 +154,19 @@ end
|
|
|
149
154
|
- Time-bounded execution
|
|
150
155
|
- Cleans up old content and expired facts
|
|
151
156
|
|
|
157
|
+
#### Embeddings (`embeddings/`)
|
|
158
|
+
- `Generator`: Built-in TF-IDF embedding generation (always available, no dependencies)
|
|
159
|
+
- `FastembedAdapter`: High-quality local embeddings via [fastembed-rb](https://github.com/khasinski/fastembed-rb) (BAAI/bge-small-en-v1.5)
|
|
160
|
+
- 384-dimensional normalized vectors (both generators produce same dimensionality)
|
|
161
|
+
- Asymmetric query/passage encoding (FastEmbed) for better retrieval accuracy
|
|
162
|
+
- `Similarity`: Cosine similarity calculations and top-k ranking
|
|
163
|
+
- Dependency injection: `Recall.new(store, embedding_generator: adapter)`
|
|
164
|
+
|
|
152
165
|
#### MCP (`mcp/`)
|
|
153
166
|
- Model Context Protocol server
|
|
154
|
-
- Exposes
|
|
167
|
+
- Exposes 19 tools including: recall, explain, promote, status, decisions, conventions, architecture, semantic search, check_setup, and more
|
|
168
|
+
- `ResponseFormatter`: Consistent MCP response formatting
|
|
169
|
+
- `SetupStatusAnalyzer`: Initialization and version status analysis
|
|
155
170
|
|
|
156
171
|
#### Hook (`hook/`)
|
|
157
172
|
- Reads JSON from stdin
|
|
@@ -164,10 +179,11 @@ end
|
|
|
164
179
|
**Components:**
|
|
165
180
|
|
|
166
181
|
#### Store (`store/`)
|
|
167
|
-
- **SQLiteStore**: Direct database access via Sequel
|
|
182
|
+
- **SQLiteStore**: Direct database access via Sequel (schema v6)
|
|
168
183
|
- **StoreManager**: Manages dual databases (global + project)
|
|
169
184
|
- **Transaction safety**: Atomic multi-step operations
|
|
170
|
-
-
|
|
185
|
+
- **WAL mode**: Write-Ahead Logging for better concurrency
|
|
186
|
+
- Schema migrations with per-migration transactions
|
|
171
187
|
|
|
172
188
|
#### FileSystem (`infrastructure/`)
|
|
173
189
|
- **FileSystem**: Real filesystem wrapper
|
|
@@ -176,8 +192,14 @@ end
|
|
|
176
192
|
- Enables testing without tempdir cleanup
|
|
177
193
|
|
|
178
194
|
#### Index (`index/`)
|
|
179
|
-
- SQLite FTS5 full-text search
|
|
180
|
-
-
|
|
195
|
+
- SQLite FTS5 for lexical full-text search
|
|
196
|
+
- Vector embeddings for semantic similarity (384-dimensional vectors)
|
|
197
|
+
- Hybrid search modes: text-only, vector-only, or both (FTS5 + vector)
|
|
198
|
+
|
|
199
|
+
#### Templates (`templates/`)
|
|
200
|
+
- Hook configuration examples (`hooks.example.json`)
|
|
201
|
+
- Output style templates (`output-styles/memory-aware.md`)
|
|
202
|
+
- Setup and configuration scaffolding
|
|
181
203
|
|
|
182
204
|
**Key Principles:**
|
|
183
205
|
- Ports and Adapters: Clear interfaces for external systems
|
|
@@ -276,6 +298,16 @@ FileSystem (write)
|
|
|
276
298
|
**Solution:** Wrap in database transactions
|
|
277
299
|
**Impact:** Data integrity guaranteed
|
|
278
300
|
|
|
301
|
+
### 4. WAL Mode for Concurrency
|
|
302
|
+
**Problem:** Database locks prevented concurrent reads during writes
|
|
303
|
+
**Solution:** Enable Write-Ahead Logging (WAL) mode in SQLite
|
|
304
|
+
**Impact:** MCP server and hooks can operate concurrently without blocking
|
|
305
|
+
|
|
306
|
+
### 5. Local Semantic Search
|
|
307
|
+
**Problem:** Traditional semantic search requires cloud API calls for embedding generation
|
|
308
|
+
**Solution:** Local ONNX model via fastembed-rb (BAAI/bge-small-en-v1.5, 384-dimensional vectors)
|
|
309
|
+
**Impact:** High-quality semantic search with no API costs, no network dependency after initial model download
|
|
310
|
+
|
|
279
311
|
## Testing Strategy
|
|
280
312
|
|
|
281
313
|
### Unit Tests
|
|
@@ -307,15 +339,17 @@ FileSystem (write)
|
|
|
307
339
|
- Scattered ENV access
|
|
308
340
|
|
|
309
341
|
### After Refactoring
|
|
310
|
-
- CLI:
|
|
311
|
-
- Tests:
|
|
342
|
+
- CLI: 41 lines (thin router, 95% reduction from original)
|
|
343
|
+
- Tests: 988 examples (257% increase)
|
|
312
344
|
- Batch queries (3 total)
|
|
313
345
|
- FileSystem abstraction
|
|
314
|
-
- Value objects
|
|
346
|
+
- Value objects (SessionId, TranscriptPath, FactId)
|
|
315
347
|
- Centralized Configuration
|
|
316
348
|
- 4 domain models with business logic
|
|
317
349
|
- 20 command classes
|
|
318
|
-
-
|
|
350
|
+
- 19 MCP tools
|
|
351
|
+
- Semantic search with local embeddings (FastEmbed + TF-IDF fallback)
|
|
352
|
+
- Schema v6 with WAL mode
|
|
319
353
|
|
|
320
354
|
## Future Improvements
|
|
321
355
|
|
|
@@ -351,11 +385,12 @@ FileSystem (write)
|
|
|
351
385
|
|
|
352
386
|
The refactored architecture provides:
|
|
353
387
|
- ✅ Clear separation of concerns
|
|
354
|
-
- ✅ High testability (
|
|
388
|
+
- ✅ High testability (988 tests)
|
|
355
389
|
- ✅ Type safety (value objects)
|
|
356
390
|
- ✅ Null safety (null objects)
|
|
357
|
-
- ✅ Performance (batch queries, in-memory FS)
|
|
391
|
+
- ✅ Performance (batch queries, in-memory FS, WAL mode)
|
|
358
392
|
- ✅ Maintainability (small, focused classes)
|
|
359
393
|
- ✅ Extensibility (easy to add commands/tools)
|
|
394
|
+
- ✅ Semantic search (local FastEmbed ONNX model, TF-IDF fallback)
|
|
360
395
|
|
|
361
396
|
The codebase now follows best practices for Ruby applications and is well-positioned for future growth.
|