Skip to content

fix(reflection): guard against empty-content and dramatic memory reduction#13

Merged
wcatz merged 1 commit intomainfrom
fix/reflection-safety-guards
Mar 15, 2026
Merged

fix(reflection): guard against empty-content and dramatic memory reduction#13
wcatz merged 1 commit intomainfrom
fix/reflection-safety-guards

Conversation

@wcatz
Copy link
Copy Markdown
Owner

@wcatz wcatz commented Mar 15, 2026

Summary

  • Filter out empty/whitespace-only content memories before ReplaceNonManual — previously [{"content":""}] passed the len > 0 check and triggered a full replace
  • Block replacement when Haiku returns fewer than 50% of existing non-manual memories (threshold: 6+) — prevents a confused reflection from wiping 47 out of 50 memories
  • 6 new tests covering: empty content filtering, data loss prevention, reasonable consolidation, small set bypass, manual-vs-non-manual counting

Test plan

  • go test ./internal/reflection/... -v — all 6 new tests pass
  • go vet ./internal/reflection/... — clean
  • Full suite passes (memory tests excluded — pre-existing FTS5 build issue)

…ction

Haiku reflection could silently wipe memories in two scenarios:
- Returning memories with empty content strings (passes len>0 check)
- Returning far fewer memories than exist (catastrophic consolidation)

Add empty-content filter (strips blank/whitespace-only memories before
processing) and a dramatic reduction guard (blocks replace when new set
is less than 50% of existing non-manual memories, threshold >= 6).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 15, 2026

Warning

Rate limit exceeded

@wcatz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78362b20-a608-4edc-ba41-70e319206488

📥 Commits

Reviewing files that changed from the base of the PR and between 977827b and 9a99349.

📒 Files selected for processing (2)
  • internal/reflection/engine.go
  • internal/reflection/engine_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/reflection-safety-guards
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@wcatz wcatz merged commit e44504b into main Mar 15, 2026
4 checks passed
@wcatz wcatz deleted the fix/reflection-safety-guards branch March 15, 2026 02:25
wcatz added a commit that referenced this pull request Mar 24, 2026
Address CodeQL path-injection alerts (#12, #13, #14):
- ClaudeMemoryDir: validate resolved path stays under ~/.claude/projects/
- ParseMemoryFile: filepath.Clean on input path
- importFromDir: filepath.Clean on dir, filepath.Base on entry names
wcatz added a commit that referenced this pull request Mar 24, 2026
Address CodeQL path-injection alerts (#12, #13, #14):
- ClaudeMemoryDir: validate resolved path stays under ~/.claude/projects/
- ParseMemoryFile: filepath.Clean on input path
- importFromDir: filepath.Clean on dir, filepath.Base on entry names
wcatz added a commit that referenced this pull request Apr 17, 2026
… hybrid search

Addresses 11 of 13 findings from MCP server audit:

🔴 Reds (crashes / data loss):
- ghost_health: p.ID[:8] panics on _global (7 chars) → min(len, 8)
- ghost_memory_delete: no ownership boundary → requires project_id, verifies via GetByIDs
- ghost_task_update: Priority int+omitempty silently clobbers to 0 → *int/*string + fetch current task

🟠 Correctness:
- ghost_task_update: omitting description wiped the field → fetch-and-merge via new GetTask store method
- ghost_search_all: FTS-only even when embedder available → SearchHybridAll (RRF) added to store
- Resource URI handlers: copy-pasted 3×, no PathUnescape → parseProjectIDFromURI helper

🟡 Quality / Design:
- EnsureProject path arg was project name, not fs path → pass "" for MCP-created projects
- ghost_memory_save / ghost_save_global: no content length limit → 2000-char cap, truncation reported
- ghost_task_list: hardcoded limit=30 → configurable Limit field (default 30, max 100)
- formatMemories: json.Marshal result silently discarded → check err before use
- ghost_memory_search description: category filter truncation undocumented → documented

Store additions: GetTask, SearchVectorAll, SearchHybridAll (all wired into MemoryStore interface).
Deferred: resolveProjectID O(n) scan (#6), registerTools monolith (#13).
wcatz added a commit that referenced this pull request Apr 17, 2026
… hybrid search (#146)

* fix(mcp): audit fixes — ownership check, panic guard, partial update, hybrid search

Addresses 11 of 13 findings from MCP server audit:

🔴 Reds (crashes / data loss):
- ghost_health: p.ID[:8] panics on _global (7 chars) → min(len, 8)
- ghost_memory_delete: no ownership boundary → requires project_id, verifies via GetByIDs
- ghost_task_update: Priority int+omitempty silently clobbers to 0 → *int/*string + fetch current task

🟠 Correctness:
- ghost_task_update: omitting description wiped the field → fetch-and-merge via new GetTask store method
- ghost_search_all: FTS-only even when embedder available → SearchHybridAll (RRF) added to store
- Resource URI handlers: copy-pasted 3×, no PathUnescape → parseProjectIDFromURI helper

🟡 Quality / Design:
- EnsureProject path arg was project name, not fs path → pass "" for MCP-created projects
- ghost_memory_save / ghost_save_global: no content length limit → 2000-char cap, truncation reported
- ghost_task_list: hardcoded limit=30 → configurable Limit field (default 30, max 100)
- formatMemories: json.Marshal result silently discarded → check err before use
- ghost_memory_search description: category filter truncation undocumented → documented

Store additions: GetTask, SearchVectorAll, SearchHybridAll (all wired into MemoryStore interface).
Deferred: resolveProjectID O(n) scan (#6), registerTools monolith (#13).

* fix(lint): add nolint:errcheck to SearchVectorAll defer rows.Close()

* chore(deps): bump google.golang.org/api from 0.272.0 to 0.275.0 (#145)

Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.272.0 to 0.275.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](googleapis/google-api-go-client@v0.272.0...v0.275.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-version: 0.275.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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