diff --git a/internal/worker/sdk/processor.go b/internal/worker/sdk/processor.go index 8cc2342..e049a89 100644 --- a/internal/worker/sdk/processor.go +++ b/internal/worker/sdk/processor.go @@ -642,25 +642,57 @@ Your response:`, // rather than actual user work. These summaries should be filtered out. func isSelfReferentialSummary(summary *models.ParsedSummary) bool { // Combine all summary fields for checking - content := strings.ToLower(summary.Request + " " + summary.Completed + " " + summary.Learned + " " + summary.NextSteps) + content := strings.ToLower(summary.Request + " " + summary.Completed + " " + summary.Learned + " " + summary.NextSteps + " " + summary.Investigated + " " + summary.Notes) // Indicators that the summary is about the memory agent, not user work selfReferentialPhrases := []string{ + // Agent references "memory extraction", "memory agent", + "extraction agent", "hook execution", "hook mechanism", + // Session meta-state "session initialization", "session setup", + "session has just started", + "session just started", "agent initialization", "no technical learnings", "no code or project work", + // Waiting states "waiting for the user", "waiting for user", "awaiting actual", "awaiting claude code", + "awaiting tool", + "awaiting user", + // Meta checkpoint references "progress checkpoint", "checkpoint request", + // Common no-work phrases + "no work has been completed", + "no work completed", + "no work done", + "no actual work", + "nothing has been completed", + "nothing completed", + // Role/guideline parroting + "role definition", + "operational guidelines", + "providing role", + "providing guidelines", + // System prompt echoes + "extract meaningful observations", + "meaningful learnings", + "analyze tool executions", + "observations for future sessions", + // Empty session indicators + "empty session", + "no substantive work", + "no meaningful work", + "just beginning", + "just begun", } matchCount := 0 @@ -685,20 +717,33 @@ func hasMeaningfulContent(assistantMsg string) bool { lowerMsg := strings.ToLower(assistantMsg) - // Skip messages that are primarily about system/hook status + // Skip messages that are primarily about system/hook status or meta-instructions skipIndicators := []string{ + // System/hook markers "hook success", "callback hook", "session start", "sessionstart", "system-reminder", + // Agent self-references "memory extraction agent", "memory agent", + "extraction agent", + // No-work indicators "no technical learnings", "waiting for", "waiting to", "no code or project work", "no substantive", + "no work has been completed", + "no work done", + "awaiting tool", + "awaiting user", + // Meta-instruction echoes + "role definition", + "operational guidelines", + "analyze tool executions", + "extract meaningful observations", } skipCount := 0 diff --git a/internal/worker/sdk/processor_test.go b/internal/worker/sdk/processor_test.go new file mode 100644 index 0000000..66059fa --- /dev/null +++ b/internal/worker/sdk/processor_test.go @@ -0,0 +1,126 @@ +package sdk + +import ( + "testing" + + "github.com/lukaszraczylo/claude-mnemonic/pkg/models" +) + +func TestIsSelfReferentialSummary(t *testing.T) { + tests := []struct { + name string + summary *models.ParsedSummary + expected bool + }{ + { + name: "meta summary about memory agent role", + summary: &models.ParsedSummary{ + Request: "Memory extraction agent role - analyze tool executions and extract meaningful observations for future sessions", + Completed: "No work has been completed yet. The session has just started with the user providing role definition and operational guidelines.", + Learned: "The system expects observations to be created from meaningful learnings during Claude Code sessions, with focus on decisions, bugs fixed, patterns discovered, project structure changes, and code modifications.", + NextSteps: "Awaiting tool executions or user requests that contain actual work performed in a Claude Code session.", + }, + expected: true, + }, + { + name: "legitimate summary about code changes", + summary: &models.ParsedSummary{ + Request: "Fix authentication bug in login handler", + Completed: "Updated the auth middleware to properly validate JWT tokens and fixed the session expiry check.", + Learned: "The JWT library requires explicit algorithm validation to prevent token substitution attacks.", + NextSteps: "Add unit tests for the authentication flow.", + }, + expected: false, + }, + { + name: "awaiting user summary", + summary: &models.ParsedSummary{ + Request: "Session initialization", + Completed: "No work completed yet.", + Learned: "Awaiting user input to begin work.", + NextSteps: "Waiting for the user to provide instructions.", + }, + expected: true, + }, + { + name: "summary about refactoring", + summary: &models.ParsedSummary{ + Request: "Refactor database connection pooling", + Completed: "Implemented connection pooling using pgxpool with max 10 connections.", + Learned: "pgxpool automatically handles connection reuse and health checks.", + NextSteps: "Run benchmarks to verify performance improvement.", + }, + expected: false, + }, + { + name: "meta summary with extraction agent mention", + summary: &models.ParsedSummary{ + Request: "Extraction agent initialization", + Completed: "No substantive work has been done.", + Learned: "The memory extraction agent analyzes tool executions.", + NextSteps: "Awaiting tool results to extract observations.", + }, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := isSelfReferentialSummary(tt.summary) + if result != tt.expected { + t.Errorf("isSelfReferentialSummary() = %v, want %v", result, tt.expected) + } + }) + } +} + +func TestHasMeaningfulContent(t *testing.T) { + tests := []struct { + name string + content string + expected bool + }{ + { + name: "empty content", + content: "", + expected: false, + }, + { + name: "too short content", + content: "Hello world", + expected: false, + }, + { + name: "meta content about memory agent", + content: `This is the memory extraction agent role definition. +The system expects you to analyze tool executions and extract meaningful observations. +No work has been completed yet. Awaiting tool results from the user's session.`, + expected: false, + }, + { + name: "legitimate code discussion", + content: `I've updated the handler.go file to fix the authentication bug. +The function validateToken() was not checking token expiry correctly. +I've added a check for exp claim and implemented proper error handling. +The changes have been tested and the build passes.`, + expected: true, + }, + { + name: "hook status messages", + content: `SessionStart:Callback hook success: Success +The memory agent is waiting for user input. +System-reminder about available tools. +No substantive work performed yet.`, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := hasMeaningfulContent(tt.content) + if result != tt.expected { + t.Errorf("hasMeaningfulContent() = %v, want %v", result, tt.expected) + } + }) + } +} diff --git a/ui/package-lock.json b/ui/package-lock.json index 8e8a4e2..bf9b37d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-mnemonic-dashboard", - "version": "v0.6.5-25-g372942e-dirty", + "version": "v0.6.4-2-gb396d05-dirty", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-mnemonic-dashboard", - "version": "v0.6.5-25-g372942e-dirty", + "version": "v0.6.4-2-gb396d05-dirty", "dependencies": { "vue": "^3.5.13" }, diff --git a/ui/package.json b/ui/package.json index 482c01f..7540476 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "claude-mnemonic-dashboard", - "version": "v0.6.5-25-g372942e-dirty", + "version": "v0.6.4-2-gb396d05-dirty", "private": true, "type": "module", "scripts": {