Skip to content

fix(mcp): track save freshness by project - #702

Draft
sparkiesweden wants to merge 1 commit into
Gentleman-Programming:mainfrom
sparkiesweden:fix/project-save-activity-nudge
Draft

fix(mcp): track save freshness by project#702
sparkiesweden wants to merge 1 commit into
Gentleman-Programming:mainfrom
sparkiesweden:fix/project-save-activity-nudge

Conversation

@sparkiesweden

@sparkiesweden sparkiesweden commented Aug 6, 2026

Copy link
Copy Markdown

Summary

  • track recent successful saves separately at project scope for read-tool nudges
  • keep per-session activity scores isolated when saves use explicit session IDs
  • suppress contradictory mem_search/mem_context warnings after mem_save or mem_session_summary
  • lazily expire project freshness entries after the configured nudge interval

Root cause

mem_save records activity under the concrete observation session ID, while mem_search and mem_context evaluate nudges under defaultSessionID(project). A fresh observation could therefore be returned together with No mem_save calls for this project ....

Updating the synthetic default session would make the warning disappear, but it breaks the existing explicit-session ActivityScore contract. This change keeps project freshness as separate state instead.

Related history: #449 improved active-session routing for saves, but does not address project-level read nudges.
Related nudge issue: #668 covers client-hook timezone and first-observation behavior, not this server-side identity mismatch.

Tracked by #703. This PR remains draft until that issue receives status:approved; Closes #703 will be added after approval.

Test plan

  • TDD regression: explicit-session save → immediate project search returns the observation without stale warning
  • context regression: explicit-session save suppresses immediate project context warning
  • session-summary regression: summary save refreshes project read activity without incrementing default-session save score
  • unit coverage for expiration and session-score isolation
  • targeted activity/save/nudge suite under Go 1.25 Docker
  • patch applies and tests cleanly against v1.20.0; live-verified through Hermes on two hosts

Notes

The full internal/mcp suite in an anonymous checkout has unrelated existing project-detection fixture failures because the checkout's detected project is not backed in test stores. The targeted affected suite is green, and the fix received two review passes with no remaining Critical or Important findings.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Project activity nudges

Layer / File(s) Summary
Activity tracking and project nudge evaluation
internal/mcp/activity.go, internal/mcp/activity_test.go
SessionActivity now stores project save timestamps. New APIs record project saves and freshness. Project nudges suppress recent saves and remove expired entries.
Project-scoped handler integration
internal/mcp/mcp.go
Search and context handlers use project-scoped nudges. Memory saves and session summaries record project freshness.
Project freshness integration coverage
internal/mcp/mcp_test.go
Tests cover explicit-session saves and session summaries that suppress stale search and context nudges.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: gentleman-programming, alan-thegentleman

Sequence Diagram(s)

sequenceDiagram
  participant MCPHandler
  participant SessionActivity
  participant ProjectStore
  MCPHandler->>ProjectStore: Resolve project
  MCPHandler->>SessionActivity: NudgeForProject(sessionID, project)
  SessionActivity-->>MCPHandler: Return project-scoped nudge
  MCPHandler->>SessionActivity: RecordProjectSave(sessionID, project)
  SessionActivity-->>MCPHandler: Update session and project freshness
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: tracking save freshness by project in MCP.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/mcp/activity_test.go`:
- Around line 82-123: Add deterministic empty-project tests covering
RecordProjectSave and RecordProjectFreshness. Verify projectSaves[""] is not
created, project nudges remain unsuppressed, and RecordProjectSave still updates
the specified session’s ActivityScore; retain the existing nonempty-project
coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9ea9877c-84f8-4650-8ff9-26775991fcea

📥 Commits

Reviewing files that changed from the base of the PR and between 509e676 and 96a87ea.

📒 Files selected for processing (4)
  • internal/mcp/activity.go
  • internal/mcp/activity_test.go
  • internal/mcp/mcp.go
  • internal/mcp/mcp_test.go

Comment on lines +82 to +123
func TestSessionActivity_ProjectSaveSuppressesNudgeWithoutMutatingDefaultScore(t *testing.T) {
now := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
a := NewSessionActivity(10 * time.Minute)
a.now = func() time.Time { return now }

project := "myproject"
defaultSID := defaultSessionID(project)
for i := 0; i < 6; i++ {
a.RecordToolCall(defaultSID)
}
now = now.Add(15 * time.Minute)
if nudge := a.NudgeForProject(defaultSID, project); nudge == "" {
t.Fatal("expected project nudge before save")
}

a.RecordProjectSave("explicit-session", project)
if nudge := a.NudgeForProject(defaultSID, project); nudge != "" {
t.Fatalf("expected project save to suppress nudge, got %q", nudge)
}
if score := a.ActivityScore(defaultSID); !strings.Contains(score, "0 saves") {
t.Fatalf("expected default session score to remain unchanged, got %q", score)
}
}

func TestSessionActivity_NudgeForProjectExpiresOldProjectSave(t *testing.T) {
now := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
a := NewSessionActivity(10 * time.Minute)
a.now = func() time.Time { return now }
project := "myproject"
defaultSID := defaultSessionID(project)
for i := 0; i < 6; i++ {
a.RecordToolCall(defaultSID)
}
a.RecordProjectSave("explicit-session", project)
now = now.Add(15 * time.Minute)
if nudge := a.NudgeForProject(defaultSID, project); nudge == "" {
t.Fatal("expected nudge after project save freshness expires")
}
if _, ok := a.projectSaves[project]; ok {
t.Fatal("expected expired project save entry to be removed")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Cover the empty-project branch.

The added tests use only a nonempty project. Add deterministic tests for RecordProjectSave and RecordProjectFreshness with project == "". Verify that neither method creates projectSaves[""] or suppresses a project nudge. Verify that RecordProjectSave still updates the specified session score.

As per path instructions, "**/*_test.go: Verify coverage of happy path, error paths, and edge cases."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mcp/activity_test.go` around lines 82 - 123, Add deterministic
empty-project tests covering RecordProjectSave and RecordProjectFreshness.
Verify projectSaves[""] is not created, project nudges remain unsuppressed, and
RecordProjectSave still updates the specified session’s ActivityScore; retain
the existing nonempty-project coverage.

Source: Path instructions

@sparkiesweden

Copy link
Copy Markdown
Author

Process update: dedicated issue #703 is open and this PR is now draft pending its status:approved label. I cannot apply the required type:bug PR label as an external contributor (HTTP 403); please add it during triage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant