From 7a583a77d1dadb0fa601a8521098f7f5c98895d9 Mon Sep 17 00:00:00 2001 From: Caleb Gross Date: Sat, 21 Mar 2026 09:13:38 -0400 Subject: [PATCH] fix: prevent consolidation from reactivating dismissed patterns, filter exclude_concepts on patterns/principles #319: UpdatePattern now includes `WHERE state != 'archived'` guard so consolidation cycles cannot overwrite a user-dismissed pattern back to active. Silent no-op when the pattern exists but is archived. #318: exclude_concepts on recall now filters patterns and abstractions in addition to memories. Uses the same conceptOverlap helper. Closes #318, closes #319 Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/mcp/server.go | 19 +++++++++++++++++++ internal/store/sqlite/patterns.go | 12 +++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/mcp/server.go b/internal/mcp/server.go index 7eb47aff..99684beb 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -637,6 +637,25 @@ func (srv *MCPServer) handleRecall(ctx context.Context, args map[string]interfac return nil, fmt.Errorf("retrieval failed: %w", err) } + // Filter patterns and abstractions by exclude_concepts + if len(excludeConcepts) > 0 { + var filteredPatterns []store.Pattern + for _, p := range result.Patterns { + if !conceptOverlap(p.Concepts, excludeConcepts) { + filteredPatterns = append(filteredPatterns, p) + } + } + result.Patterns = filteredPatterns + + var filteredAbstractions []store.Abstraction + for _, a := range result.Abstractions { + if !conceptOverlap(a.Concepts, excludeConcepts) { + filteredAbstractions = append(filteredAbstractions, a) + } + } + result.Abstractions = filteredAbstractions + } + // Save traversal data and access snapshot for feedback loop var retrievedIDs []string var snapshot []store.AccessSnapshotEntry diff --git a/internal/store/sqlite/patterns.go b/internal/store/sqlite/patterns.go index 2fd77f93..f4387b42 100644 --- a/internal/store/sqlite/patterns.go +++ b/internal/store/sqlite/patterns.go @@ -67,7 +67,7 @@ func (s *SQLiteStore) UpdatePattern(ctx context.Context, p store.Pattern) error SET pattern_type = ?, title = ?, description = ?, evidence_ids = ?, strength = ?, project = ?, concepts = ?, embedding = ?, access_count = ?, last_accessed = ?, state = ?, updated_at = ? - WHERE id = ?`, + WHERE id = ? AND state != 'archived'`, p.PatternType, p.Title, p.Description, @@ -88,6 +88,16 @@ func (s *SQLiteStore) UpdatePattern(ctx context.Context, p store.Pattern) error rowsAffected, _ := result.RowsAffected() if rowsAffected == 0 { + // Check if the pattern exists but is archived (dismissed by user). + // In that case, the WHERE state != 'archived' guard prevented the update — this is expected. + var state string + row := s.db.QueryRowContext(ctx, `SELECT state FROM patterns WHERE id = ?`, p.ID) + if err := row.Scan(&state); err != nil { + return fmt.Errorf("pattern with id %s: %w", p.ID, store.ErrNotFound) + } + if state == "archived" { + return nil // silently skip — pattern was dismissed + } return fmt.Errorf("pattern with id %s: %w", p.ID, store.ErrNotFound) } return nil