Skip to content

feat(recall): set keyword ranking weight from benchmark data (v2.32.0) - #118

Merged
Lyellr88 merged 6 commits into
MARM-mainfrom
release/v2.32.0
Jul 28, 2026
Merged

feat(recall): set keyword ranking weight from benchmark data (v2.32.0)#118
Lyellr88 merged 6 commits into
MARM-mainfrom
release/v2.32.0

Conversation

@Lyellr88

@Lyellr88 Lyellr88 commented Jul 28, 2026

Copy link
Copy Markdown
Owner

v2.31.0 activated keyword candidates for natural-language recall but left keyword scores out of ranking, because the old 0.35 weight had been chosen while that path never ran. This sweeps it and turns it on.

Defaults, both measured on LoCoMo (1,977 questions, 5,882 memories, top-5):

  • HYBRID_SEARCH_TEXT_WEIGHT 0.0 -> 0.05. Swept over 13 points; any-hit peaks across a broad 0.04-0.08 plateau at 62.0-62.5% against 57.4% with keyword scoring off and 57.6% at the old 0.35. 0.05 is the plateau centre rather than the argmax, so the default is not fitted to one corpus. High weights are actively harmful: single-hop falls 56.9% -> 47.3% between 0.05 and 0.35.
  • FTS_CANDIDATE_LIMIT 50 -> 200. This was the lever on the multi-hop regression v2.31.0 introduced; 200 recovers multi-hop any-hit 34.8% -> 39.3%, matching the pre-v2.31.0 baseline, lifts single-hop 1.1pp, leaves adversarial precision unchanged, and costs ~3ms per recall. 500 buys 1.1pp more multi-hop but erodes the adversarial gain, since a pool that large stops narrowing anything.

Combined vs v2.31.0 on the same corpus: any-hit 57.4% -> 62.5%, all-hit 47.6% -> 52.6%, evidence recall 51.9% -> 56.9%. Every category improves.

Also fixes non-reproducible candidate selection, found because two identical benchmark runs scored 0.5pp apart while disagreeing on 18 questions. Ties at the candidate cutoff are the normal case (53.7% of queries) and SQLite ordered those rows differently between processes, so the pool varied across restarts. Candidate selection now breaks ties on memory ID. Accuracy is unchanged within measurement error; results are now reproducible, which also drops the benchmark's resolution floor from ~0.5pp to ~0.1pp. The fusion bake-off compares methods that can differ by less than the old floor, so it could not have been measured on the previous harness.

New setting FTS_LONE_HIT_SCORE (default 1.0) sets the keyword score for a candidate set too degenerate to rank. It ships as a no-op: swept over 0.0/0.3/0.5/1.0 with no measurable effect, because only 1 of 1,982 queries produced such a set on a store this size. Exposed for small stores, where a query matching exactly one memory is routine. The spec had called the previous hardcoded 1.0 an active ranking bug; measurement says otherwise, since under wide OR a degenerate set means one memory held the only match for any query term, which is discriminative rather than weak evidence.

No schema change, no migration, no re-embedding. Recall ordering changes by design; explicit env overrides are unaffected.

Summary by CodeRabbit

  • New Features
    • Improved keyword/semantic ranking with updated default weighting.
    • Increased the default keyword candidate limit from 50 to 200.
    • Added FTS_LONE_HIT_SCORE to control scoring for single/tied keyword matches.
    • Retrieval diagnostics now display the single-match score.
  • Bug Fixes
    • Made keyword candidate selection deterministic on tie scores for consistent results across runs.
  • Documentation
    • Updated configuration, FAQ, and installation materials to reflect v2.32.0 and the new default settings.

v2.31.0 activated keyword candidates for natural-language recall but left
keyword scores out of ranking, because the old 0.35 weight had been chosen
while that path never ran. This sweeps it and turns it on.

Defaults, both measured on LoCoMo (1,977 questions, 5,882 memories, top-5):
- HYBRID_SEARCH_TEXT_WEIGHT 0.0 -> 0.05. Swept over 13 points; any-hit peaks
  across a broad 0.04-0.08 plateau at 62.0-62.5% against 57.4% with keyword
  scoring off and 57.6% at the old 0.35. 0.05 is the plateau centre rather
  than the argmax, so the default is not fitted to one corpus. High weights
  are actively harmful: single-hop falls 56.9% -> 47.3% between 0.05 and 0.35.
- FTS_CANDIDATE_LIMIT 50 -> 200. This was the lever on the multi-hop
  regression v2.31.0 introduced; 200 recovers multi-hop any-hit 34.8% ->
  39.3%, matching the pre-v2.31.0 baseline, lifts single-hop 1.1pp, leaves
  adversarial precision unchanged, and costs ~3ms per recall. 500 buys 1.1pp
  more multi-hop but erodes the adversarial gain, since a pool that large
  stops narrowing anything.

Combined vs v2.31.0 on the same corpus: any-hit 57.4% -> 62.5%, all-hit
47.6% -> 52.6%, evidence recall 51.9% -> 56.9%. Every category improves.

Also fixes non-reproducible candidate selection, found because two identical
benchmark runs scored 0.5pp apart while disagreeing on 18 questions. Ties at
the candidate cutoff are the normal case (53.7% of queries) and SQLite ordered
those rows differently between processes, so the pool varied across restarts.
Candidate selection now breaks ties on memory ID. Accuracy is unchanged within
measurement error; results are now reproducible, which also drops the
benchmark's resolution floor from ~0.5pp to ~0.1pp. The fusion bake-off
compares methods that can differ by less than the old floor, so it could not
have been measured on the previous harness.

New setting FTS_LONE_HIT_SCORE (default 1.0) sets the keyword score for a
candidate set too degenerate to rank. It ships as a no-op: swept over
0.0/0.3/0.5/1.0 with no measurable effect, because only 1 of 1,982 queries
produced such a set on a store this size. Exposed for small stores, where a
query matching exactly one memory is routine. The spec had called the previous
hardcoded 1.0 an active ranking bug; measurement says otherwise, since under
wide OR a degenerate set means one memory held the only match for any query
term, which is discriminative rather than weak evidence.

No schema change, no migration, no re-embedding. Recall ordering changes by
design; explicit env overrides are unaffected.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Lyellr88, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 678d74a3-c279-4b9f-81e4-9554854e328e

📥 Commits

Reviewing files that changed from the base of the PR and between afc7491 and 59f234d.

📒 Files selected for processing (4)
  • CONTRIBUTING.md
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
  • scripts/benchmarking/performance/bench_hotpath.py
📝 Walkthrough

Walkthrough

Changes

MARM v2.32.0 updates hybrid retrieval defaults, adds configurable BM25 lone-hit scoring, stabilizes FTS candidate tie-breaking, exposes the setting through diagnostics, aligns benchmarks, refreshes documentation, and synchronizes release metadata.

Retrieval tuning and validation

Layer / File(s) Summary
Scoring, configuration, candidate selection, and validation
marm-mcp-server/marm_mcp_server/config/settings.py, marm-mcp-server/marm_mcp_server/core/memory_scoring.py, marm-mcp-server/tests/test_hybrid_search.py
Defaults change to HYBRID_SEARCH_TEXT_WEIGHT=0.05 and FTS_CANDIDATE_LIMIT=200; FTS_LONE_HIT_SCORE is added with clamping, BM25 normalization accepts it, FTS ties use memory ID ordering, and tests cover these behaviors.
Diagnostics and retrieval documentation
marm-mcp-server/marm_mcp_server/services/*, README.md, docs/FAQ.md, marm-mcp-server/marm_mcp_server/resources/marm-docs/*, CHANGELOG.md
Doctor status and CLI output include the lone-hit score, while retrieval defaults and v2.32.0 release notes are updated.
Benchmark candidate-cap alignment
scripts/benchmarking/performance/bench_hotpath.py
Benchmark FTS matching uses the wide query builder and reports the configured FTS_CANDIDATE_LIMIT instead of a hard-coded cap.
Version and distribution metadata
marm-mcp-server/pyproject.toml, marm-mcp-server/server.json, marm-mcp-server/Dockerfile, marm-mcp-server/docker-compose.yml, marm-mcp-server/marm_mcp_server/*, docs/INSTALL-*, docs/TECHNICAL-OVERVIEW.md
Package, container, manifest, runtime, and installation documentation versions are synchronized to 2.32.0.
Security policy contact
SECURITY.md
The vulnerability reporting email address is updated.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
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.

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f863d6d43e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# roughly 3ms more per recall. 500 buys another 1.1pp of multi-hop but starts
# giving back the adversarial gain, because a pool that large stops acting as a
# precision gate.
_raw_fcl = _safe_int("FTS_CANDIDATE_LIMIT", 200)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the hot-path benchmark for the 200-candidate default

With this new default, recall_similar fetches up to 200 candidates, but scripts/benchmarking/performance/bench_hotpath.py:297,367,379 still clamps the diagnostic with min(matched, 50) and labels it /50 and “capped at 50.” Consequently, queries with at least 50 matches always appear to saturate the production pool even when only a quarter of the new pool is filled, making the benchmark output misleading for the performance and breadth change this release advertises; derive the reporting cap from FTS_CANDIDATE_LIMIT instead.

AGENTS.md reference: AGENTS.md:L76-L76

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
marm-mcp-server/tests/test_hybrid_search.py (1)

924-936: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test reimplements the clamp formula instead of exercising the real settings path.

This asserts _safe_float output against a manually re-derived max(0.0, min(1.0, ...)), rather than reloading settings.py and checking the actual FTS_LONE_HIT_SCORE constant (and its stderr warning). A divergence between the documented formula and the real clamp/warning logic in settings.py wouldn't be caught here.

♻️ Suggested approach exercising the real module path
-def test_fts_lone_hit_score_clamped_to_unit_range(monkeypatch):
-    """Out-of-range values clamp rather than propagate an invalid weight into
-    ranking, matching the neighbouring settings' clamp-and-warn contract."""
-    from marm_mcp_server.config.settings import _safe_float
-
-    monkeypatch.setenv("FTS_LONE_HIT_SCORE", "2.5")
-    assert max(0.0, min(1.0, _safe_float("FTS_LONE_HIT_SCORE", 1.0))) == 1.0
-
-    monkeypatch.setenv("FTS_LONE_HIT_SCORE", "-1")
-    assert max(0.0, min(1.0, _safe_float("FTS_LONE_HIT_SCORE", 1.0))) == 0.0
-
-    monkeypatch.setenv("FTS_LONE_HIT_SCORE", "not-a-number")
-    assert _safe_float("FTS_LONE_HIT_SCORE", 1.0) == 1.0
+def test_fts_lone_hit_score_clamped_to_unit_range(monkeypatch):
+    """Out-of-range values clamp via the real settings module, matching the
+    neighbouring settings' clamp-and-warn contract."""
+    import importlib
+    from marm_mcp_server.config import settings
+
+    monkeypatch.setenv("FTS_LONE_HIT_SCORE", "2.5")
+    importlib.reload(settings)
+    assert settings.FTS_LONE_HIT_SCORE == 1.0
+
+    monkeypatch.setenv("FTS_LONE_HIT_SCORE", "-1")
+    importlib.reload(settings)
+    assert settings.FTS_LONE_HIT_SCORE == 0.0
+
+    monkeypatch.delenv("FTS_LONE_HIT_SCORE")
+    importlib.reload(settings)  # restore defaults for other tests
🤖 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 `@marm-mcp-server/tests/test_hybrid_search.py` around lines 924 - 936, Update
test_fts_lone_hit_score_clamped_to_unit_range to reload the real settings module
after each monkeypatched FTS_LONE_HIT_SCORE value, then assert the module’s
actual FTS_LONE_HIT_SCORE constant and captured stderr warning. Remove the
manually reimplemented max/min assertions and preserve coverage for high, low,
and invalid values using the settings module path.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@marm-mcp-server/tests/test_hybrid_search.py`:
- Around line 924-936: Update test_fts_lone_hit_score_clamped_to_unit_range to
reload the real settings module after each monkeypatched FTS_LONE_HIT_SCORE
value, then assert the module’s actual FTS_LONE_HIT_SCORE constant and captured
stderr warning. Remove the manually reimplemented max/min assertions and
preserve coverage for high, low, and invalid values using the settings module
path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 220f443a-1470-41c5-865f-a05e1d6793c7

📥 Commits

Reviewing files that changed from the base of the PR and between 2b6cfd0 and f863d6d.

📒 Files selected for processing (22)
  • CHANGELOG.md
  • README.md
  • docs/FAQ.md
  • docs/INSTALL-DOCKER.md
  • docs/INSTALL-LINUX.md
  • docs/INSTALL-PLATFORMS.md
  • docs/INSTALL-WINDOWS.md
  • docs/TECHNICAL-OVERVIEW.md
  • marm-mcp-server/Dockerfile
  • marm-mcp-server/README.md
  • marm-mcp-server/docker-compose.yml
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/pyproject.toml
  • marm-mcp-server/server.json
  • marm-mcp-server/tests/test_hybrid_search.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{py,json,md,toml,yml,yaml,Dockerfile}

📄 CodeRabbit inference engine (AGENTS.md)

Keep changes surgical: modify only what the task requires, match existing style, and preserve behavior during refactors.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/FAQ.md
  • docs/INSTALL-LINUX.md
  • marm-mcp-server/marm_mcp_server/server.py
  • docs/TECHNICAL-OVERVIEW.md
  • marm-mcp-server/pyproject.toml
  • docs/INSTALL-WINDOWS.md
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/docker-compose.yml
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md
  • CHANGELOG.md
  • marm-mcp-server/server.json
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • README.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
**/*.{md,py}

📄 CodeRabbit inference engine (AGENTS.md)

When adding or removing an MCP tool, update every full tool list and tool count in the README, protocol documentation, FAQ, and maintained marm-docs copies; add tests covering both transports.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/FAQ.md
  • docs/INSTALL-LINUX.md
  • marm-mcp-server/marm_mcp_server/server.py
  • docs/TECHNICAL-OVERVIEW.md
  • docs/INSTALL-WINDOWS.md
  • marm-mcp-server/marm_mcp_server/__init__.py
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md
  • CHANGELOG.md
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • README.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Never commit changes without an explicit user request.
Use semantic versioning: MAJOR for breaking changes, MINOR for new tools, parameters, or features, and PATCH for fixes and documentation updates.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/FAQ.md
  • docs/INSTALL-LINUX.md
  • marm-mcp-server/marm_mcp_server/server.py
  • docs/TECHNICAL-OVERVIEW.md
  • marm-mcp-server/Dockerfile
  • marm-mcp-server/pyproject.toml
  • docs/INSTALL-WINDOWS.md
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/docker-compose.yml
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md
  • CHANGELOG.md
  • marm-mcp-server/server.json
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • README.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Only flag documentation issues that are materially wrong, misleading for installation/release behavior, or inconsistent with live MCP behavior. Skip style, phrasing, formatting, and wording preferences.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/FAQ.md
  • docs/INSTALL-LINUX.md
  • docs/TECHNICAL-OVERVIEW.md
  • docs/INSTALL-WINDOWS.md
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md
  • CHANGELOG.md
  • README.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
marm-mcp-server/marm_mcp_server/server.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep HTTP MCP tools listed in MCP_TOOL_OPERATIONS; tools absent from this whitelist must not be exposed over HTTP.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
marm-mcp-server/marm_mcp_server/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/marm_mcp_server/**/*.py: All memory writes must use the serialized asynchronous write queue; do not add bypass paths.
A semantic-store failure during marm_log_entry must never fail the corresponding log write.
Keep the memory database and isolated concept-graph database on separate connection pools; never share connections between them.
Graph and concept failures must never break the seven core memory tools.
marm_smart_recall must keep primary memory ranking authoritative; graph enrichment is bounded, read-only, fail-open, and must be trimmed before primary results when enforcing response limits.
Use one lazy-loaded jinaai/jina-embeddings-v2-small-en encoder with 512 dimensions, serialized behind a lock; writes must succeed if the encoder is unavailable.
Keep orchestration in its current owner file and extract modules only at real boundaries consistent with the existing endpoint split.
Comments must be minimal, explain only non-obvious reasons, and never narrate the next line.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
marm-mcp-server/{pyproject.toml,server.json,marm_mcp_server/__init__.py,marm_mcp_server/config/settings.py,marm_mcp_server/server.py,Dockerfile,docker-compose.yml}

📄 CodeRabbit inference engine (AGENTS.md)

When bumping the version, update every listed package, manifest, server, Docker, and configuration version occurrence, and audit with python scripts/find-versions.py.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/Dockerfile
  • marm-mcp-server/pyproject.toml
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/docker-compose.yml
  • marm-mcp-server/server.json
  • marm-mcp-server/marm_mcp_server/config/settings.py
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: Prioritize runtime correctness, async/concurrency safety, SQLite transaction safety, auth/rate-limit behavior, release-breaking packaging issues, and MCP protocol compatibility.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/marm_mcp_server/services/cli_output.py
  • marm-mcp-server/marm_mcp_server/core/memory_scoring.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
marm-mcp-server/{server.py,server_stdio.py,server.json}

📄 CodeRabbit inference engine (AGENTS.md)

When adding or removing an MCP tool, update the endpoint implementation, HTTP route and whitelist, STDIO registration/wrapper, and server.json tools array.

Files:

  • marm-mcp-server/server.json
marm-mcp-server/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/tests/**/*.py: Run tests with pytest from marm-mcp-server/; tests should exercise real FastAPI endpoints and real SQLite, using mocks only when they preserve at least 95% fidelity and provide meaningful speedup.
Every new MARM Console API route must have at least one happy-path FastAPI response-contract test with the MCP adapter stubbed.
Do not write existence-check or coded-to-pass tests; prefer deep tests exercising real paths over broad shallow coverage.
Use pytest.mark.skip only for genuinely unavailable dependencies, never merely to avoid implementation effort.

Files:

  • marm-mcp-server/tests/test_hybrid_search.py
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: Focus on tests that are flaky, non-isolated, incorrectly asserting behavior, or missing coverage for a changed high-risk path. Skip minor naming, comments, and layout preferences.

Files:

  • marm-mcp-server/tests/test_hybrid_search.py
🔇 Additional comments (23)
marm-mcp-server/marm_mcp_server/config/settings.py (2)

253-328: LGTM!


147-147: Version bump is already propagated to all mandated files.

marm-mcp-server/marm_mcp_server/core/memory_scoring.py (1)

6-27: LGTM!

Also applies to: 223-283

marm-mcp-server/tests/test_hybrid_search.py (1)

163-187: LGTM!

Also applies to: 777-779, 813-923

CHANGELOG.md (1)

5-26: LGTM!

README.md (1)

8-8: LGTM!

Also applies to: 844-844, 982-987

docs/FAQ.md (1)

153-153: LGTM!

marm-mcp-server/README.md (1)

10-10: LGTM!

Also applies to: 844-844, 982-987

marm-mcp-server/marm_mcp_server/resources/marm-docs/FAQ.md (1)

153-153: LGTM!

marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md (1)

1-1: LGTM!

Also applies to: 810-810, 948-953

marm-mcp-server/marm_mcp_server/services/runtime_status.py (1)

24-24: LGTM!

Also applies to: 234-234

marm-mcp-server/marm_mcp_server/services/cli_output.py (1)

102-102: LGTM!

docs/INSTALL-DOCKER.md (1)

5-5: LGTM!

docs/INSTALL-LINUX.md (1)

5-5: LGTM!

Also applies to: 323-323

docs/INSTALL-PLATFORMS.md (1)

1-1: LGTM!

docs/INSTALL-WINDOWS.md (1)

5-5: LGTM!

Also applies to: 297-297

docs/TECHNICAL-OVERVIEW.md (1)

3-3: LGTM!

marm-mcp-server/Dockerfile (1)

76-76: LGTM!

marm-mcp-server/docker-compose.yml (1)

8-8: LGTM!

Also applies to: 21-21

marm-mcp-server/marm_mcp_server/__init__.py (1)

17-20: LGTM!

marm-mcp-server/marm_mcp_server/server.py (1)

8-8: LGTM!

marm-mcp-server/pyproject.toml (1)

7-7: LGTM!

marm-mcp-server/server.json (1)

6-6: LGTM!

Also applies to: 20-26

Lyellr88 added 2 commits July 28, 2026 06:24
…uilder

Addresses PR #118 review.

The hot-path benchmark's "FTS Hits" column was wrong in two ways, both of
which made it useless for judging exactly the breadth change this release
ships:

- It clamped and labelled against a hardcoded 50, so with the new default of
  200 any query matching 50+ candidates reported a saturated "50/50" while a
  quarter of the real pool was filled. Cap and labels now derive from
  FTS_CANDIDATE_LIMIT. Measured effect: a 100-row corpus averages 85.8
  candidates, previously reported as 50/50.
- It counted matches for _safe_fts_query while timing recall_similar, which
  runs _wide_fts_query. The three benchmark queries are all non-exact, so the
  diagnostic had been describing a query the measured path never issues since
  v2.31.0. Now uses the same builder as the path being timed.

Also replaces the FTS_LONE_HIT_SCORE clamp test, which re-derived
max(0.0, min(1.0, ...)) in the test body rather than exercising real code, so
a divergence between the two would not have been caught. The clamp and its
warning now live in a _safe_unit_float helper the setting is built from and
the test calls directly, covering out-of-range, unparseable, in-range, and
unset input. Reloading settings to reach the constant is not usable here: it
raises ImportError once another test has evicted the module, which is why
_safe_choice and _csv_frozenset are structured the same way. Five other
settings still inline the same clamp and are candidates for the helper, left
alone as pre-existing and out of scope.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@scripts/benchmarking/performance/bench_hotpath.py`:
- Line 303: Update the FTS hit counting and reporting logic around fts_hits and
the corresponding benchmark output to use the production effective candidate
cap, max(5, FTS_CANDIDATE_LIMIT), consistently. Apply the same cap at the
additional indicated occurrences so metrics and denominators match
recall_similar(..., limit=5).

In `@SECURITY.md`:
- Line 7: Replace the personal Gmail contact in SECURITY.md with a monitored
vulnerability-reporting alias on an organization-controlled domain. Use the
established security mailbox if one exists; otherwise, define an
organization-owned alias with appropriate monitoring and access controls.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 069b1fb8-1d1b-4ee5-a4f4-4a9ad49e554c

📥 Commits

Reviewing files that changed from the base of the PR and between f863d6d and afc7491.

📒 Files selected for processing (4)
  • SECURITY.md
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
  • scripts/benchmarking/performance/bench_hotpath.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/tests/test_hybrid_search.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{py,json,md,toml,yml,yaml,Dockerfile}

📄 CodeRabbit inference engine (AGENTS.md)

Keep changes surgical: modify only what the task requires, match existing style, and preserve behavior during refactors.

Files:

  • SECURITY.md
  • scripts/benchmarking/performance/bench_hotpath.py
**/*.{md,py}

📄 CodeRabbit inference engine (AGENTS.md)

When adding or removing an MCP tool, update every full tool list and tool count in the README, protocol documentation, FAQ, and maintained marm-docs copies; add tests covering both transports.

Files:

  • SECURITY.md
  • scripts/benchmarking/performance/bench_hotpath.py
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Never commit changes without an explicit user request.
Use semantic versioning: MAJOR for breaking changes, MINOR for new tools, parameters, or features, and PATCH for fixes and documentation updates.

Files:

  • SECURITY.md
  • scripts/benchmarking/performance/bench_hotpath.py
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Only flag documentation issues that are materially wrong, misleading for installation/release behavior, or inconsistent with live MCP behavior. Skip style, phrasing, formatting, and wording preferences.

Files:

  • SECURITY.md
scripts/benchmarking/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Do not publish performance claims that cannot be supported by the repository benchmark scripts.

Files:

  • scripts/benchmarking/performance/bench_hotpath.py
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: Prioritize runtime correctness, async/concurrency safety, SQLite transaction safety, auth/rate-limit behavior, release-breaking packaging issues, and MCP protocol compatibility.

Files:

  • scripts/benchmarking/performance/bench_hotpath.py
🔇 Additional comments (2)
scripts/benchmarking/performance/bench_hotpath.py (2)

40-46: LGTM!


253-256: LGTM!

Comment thread scripts/benchmarking/performance/bench_hotpath.py Outdated
Comment thread SECURITY.md
Please report security issues privately by emailing:

**support@marmsystems.com**
**lyellr88@gmail.com**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SECURITY.md relevant lines =="
if [ -f SECURITY.md ]; then
  nl -ba SECURITY.md | sed -n '1,40p'
else
  echo "SECURITY.md not found"
fi

echo
echo "== README/repository docs mentioning vulnerability/security reporting or email =="
rg -n --hidden -i 'security advisories|security policy|vulnerability|vulnerabilities|lyellr88@gmail.com|`@github`\.com|email|reporting' -g 'SECURITY.md' -g '*.md' -g 'README*' . 2>/dev/null || true

echo
echo "== repo metadata =="
git rev-parse --show-toplevel 2>/dev/null || true
git remote -v 2>/dev/null || true

Repository: Lyellr88/marm-memory

Length of output: 231


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import os, re
path='SECURITY.md'
content=open(path, 'r', encoding='utf-8').read()
emails=re.findall(r'[\w.+-]+@[\w.-]+', content)
print("EMAILS_IN_SECURITY_MD")
for e in emails: print(e)
print("HAS_GMAIL_EQUIVALENT_OR_ORG_DOMAIN_MAILBOXS")
print(any(e.endswith('`@gmail.com`') for e in emails))
print("EMAIL_COUNT", len(emails))
PY

Repository: Lyellr88/marm-memory

Length of output: 262


Security Misconfiguration (CWE-16)

Reachability: External

Use an organization-controlled vulnerability mailbox.

Use a monitored alias on an owned organization/project domain instead of lyellr88@gmail.com, unless this Gmail address is explicitly covered by documented monitoring, access control, retention, and continuity controls.

🤖 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 `@SECURITY.md` at line 7, Replace the personal Gmail contact in SECURITY.md
with a monitored vulnerability-reporting alias on an organization-controlled
domain. Use the established security mailbox if one exists; otherwise, define an
organization-owned alias with appropriate monitoring and access controls.

Lyellr88 added 3 commits July 28, 2026 06:36
…e cap

Second round of PR #118 review.

- Report keyword-pool saturation against max(RECALL_LIMIT, FTS_CANDIDATE_LIMIT),
  the cap recall_similar actually applies, rather than FTS_CANDIDATE_LIMIT
  alone. Identical at any sane setting; they diverge only when the limit is
  configured below the requested top-K, where production still fetches top-K
  rows and the old denominator would have been one production never used. Both
  values are now named constants so the counter and the two labels cannot drift
  apart again, and the timed recalls use the same RECALL_LIMIT they report against.

- Add a test that imports settings.py in a clean subprocess with
  FTS_LONE_HIT_SCORE set, asserting the constant the rest of the code imports
  and the stderr warning. The previous test exercised _safe_unit_float only, so
  changing the shipped assignment to _safe_float would have dropped the clamp
  with both tests still green; verified the new test fails on exactly that
  substitution while the helper test passes. A fresh interpreter is used because
  monkeypatching after import is too late and importlib.reload raises
  ImportError once another test has evicted the module. This also covers a
  non-default in-range value, which nothing previously did.

- Stop describing the degenerate-set diagnostic as a count of LoCoMo or
  benchmark queries. 1,982 is the number of offline FTS calls the probe made;
  the harness scores 1,977 questions. Different filters, different numbers, and
  conflating them overstated what the benchmark measured. Docs and changelog
  already said this correctly; settings.py and the test docstring did not.
Addresses PR #118 review.

The two full-scan calls passed a literal 5 while the production calls they are
timed against used RECALL_LIMIT. No behavior change today since the constant is
5, but the two arms of a comparison should not read their top-K from different
places: changing RECALL_LIMIT would have silently made the baseline and the
production path request different limits, and the resulting speedup ratio would
have compared two different workloads.

Verified both timed paths still run: full scan 4.3ms/17.1ms and production
hybrid 6.1ms/11.0ms at N=100/500, keyword pool unchanged at 85.8 and 200.0.
@Lyellr88
Lyellr88 merged commit 57546da into MARM-main Jul 28, 2026
7 checks passed
@Lyellr88
Lyellr88 deleted the release/v2.32.0 branch July 29, 2026 09:37
@coderabbitai coderabbitai Bot mentioned this pull request Jul 30, 2026
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