Skip to content

feat(memory): align semantic and KB recall - #656

Merged
delete-cloud merged 2 commits into
mainfrom
feat/kb-defer-on-semantic-memory
Jun 30, 2026
Merged

delete-cloud merged 2 commits into
mainfrom
feat/kb-defer-on-semantic-memory

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Jun 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • make semantic memory and KB retrieval use the same current runtime prompt instead of stale tape user text
  • add a scoped semantic grounding marker so KB can optionally defer when semantic memory already grounded the same query
  • lazily derive durable TopicStore summaries into a TopicRangeIndex, skipping recall-unsafe topics and caching per plugin instance
  • expose kb.deferWhenSemanticMemoryHits in Helm and fail fast when custom plugin ordering would put KB before semantic memory

Verification

  • uv run pytest tests/coding_agent/plugins/test_kb_plugin.py tests/coding_agent/plugins/test_semantic_memory.py tests/coding_agent/test_context_system_smoke.py tests/coding_agent/test_bootstrap.py tests/deploy/test_helm_chart.py -v
  • uv run ruff check src/coding_agent/topics/range_index_builder.py src/coding_agent/plugins/semantic_memory.py src/coding_agent/plugins/kb.py src/coding_agent/core/app.py tests/coding_agent/plugins/test_semantic_memory.py tests/coding_agent/plugins/test_kb_plugin.py tests/coding_agent/test_context_system_smoke.py tests/coding_agent/test_bootstrap.py tests/deploy/test_helm_chart.py
  • uv run ruff format --check src/coding_agent/topics/range_index_builder.py src/coding_agent/plugins/semantic_memory.py src/coding_agent/plugins/kb.py src/coding_agent/core/app.py tests/coding_agent/plugins/test_semantic_memory.py tests/coding_agent/plugins/test_kb_plugin.py tests/coding_agent/test_context_system_smoke.py tests/coding_agent/test_bootstrap.py tests/deploy/test_helm_chart.py
  • local subagent review loop reached No P1/P2 after fixes

Notes

  • PR is opened as draft per lifecycle default; ready/merge should wait for CI and remote review gates.

Summary by CodeRabbit

  • New Features

    • Added an option to defer KB lookups when semantic memory already has a relevant match.
    • Improved memory recall so recent runtime prompts are used more consistently during context building.
    • Semantic memory can now derive topic indexing automatically from stored topics.
  • Bug Fixes

    • Prevented stale KB results from overriding fresher runtime guidance.
    • Enforced safer plugin ordering when both semantic memory and KB are enabled.

Use the current runtime prompt for both semantic memory and KB retrieval, add an optional KB defer gate when semantic memory has grounded the same query, and lazily derive durable topic range indexes for semantic recall.

Verification: uv run pytest tests/coding_agent/plugins/test_kb_plugin.py tests/coding_agent/plugins/test_semantic_memory.py tests/coding_agent/test_context_system_smoke.py tests/coding_agent/test_bootstrap.py tests/deploy/test_helm_chart.py -v

Verification: uv run ruff check/format --check on touched files
@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

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

Next review available in: 44 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5c9faae3-5749-4c5b-bdd9-a41845f678c0

📥 Commits

Reviewing files that changed from the base of the PR and between 36e1c36 and 4a3f54f.

📒 Files selected for processing (1)
  • coding-agent/tests/coding_agent/test_context_system_smoke.py
📝 Walkthrough

Walkthrough

Introduces a defer_when_semantic_memory_hits flag for KBPlugin. When enabled, SemanticMemoryPlugin writes a grounding marker (query digest, tape length, hit count) into PipelineContext.config after each recall; KBPlugin reads and pops that marker to skip KB retrieval when semantic memory already satisfied the query. A new build_topic_range_index_from_store utility enables lazy topic index derivation. App wiring validates that semantic_memory precedes kb in the plugin order when deferral is enabled. Helm and all tests are updated accordingly.

Changes

Semantic memory grounding marker and KB deferral

Layer / File(s) Summary
TopicRangeIndex builder from store
coding-agent/src/coding_agent/topics/range_index_builder.py
New module defining TopicRangeListingStore protocol, TopicRangeIndexBuildResult dataclass, and build_topic_range_index_from_store async function that fetches finalized topics and indexes them with skip counting.
SemanticMemoryPlugin: lazy index derivation and grounding marker
coding-agent/src/coding_agent/plugins/semantic_memory.py
Adds lazy topic index derivation via build_topic_range_index_from_store, clears a stale grounding marker at build_context entry, writes a new SEMANTIC_MEMORY_GROUNDING_MARKER_KEY marker (query digest, tape length, hit count) after planning, and exposes semantic_grounding_query_digest. _validate_topic_index now preserves None to enable the lazy path.
KBPlugin: runtime query selection and semantic deferral
coding-agent/src/coding_agent/plugins/kb.py
Adds defer_when_semantic_memory_hits parameter (validated bool). build_context selects the query from the latest eligible runtime message, calls _take_semantic_memory_hit_count (pops grounding marker), and skips KB retrieval when hit count > 0 and deferral is enabled. New helpers extract runtime message text.
app.py wiring: config parsing and plugin order validation
coding-agent/src/coding_agent/core/app.py
Parses kb.defer_when_semantic_memory_hits with bool validation, passes it to KBPlugin, and calls _validate_kb_semantic_plugin_order which raises ValueError when semantic_memory does not precede kb in the enabled plugin list.
Helm config: defer_when_semantic_memory_hits flag
coding-agent/helm/values.yaml, coding-agent/helm/templates/configmap-agent-config.yaml
Adds kb.deferWhenSemanticMemoryHits (default false) in values and injects it as defer_when_semantic_memory_hits into the kb TOML block.
SemanticMemoryPlugin tests
coding-agent/tests/coding_agent/plugins/test_semantic_memory.py
FakeTopicStore gains list_topics tracking. New tests verify lazy topic index derivation, unsafe topic skipping, grounding marker population, and stale marker clearing.
KBPlugin tests
coding-agent/tests/coding_agent/plugins/test_kb_plugin.py
New tests cover runtime message query preference, system-notice exclusion, and all deferral scenarios (skip on hit, run on zero hits, stale digest, invalid hit_count type).
Bootstrap and smoke tests
coding-agent/tests/coding_agent/test_bootstrap.py, coding-agent/tests/coding_agent/test_context_system_smoke.py
Bootstrap tests assert flag propagation and ordering rejection. Smoke test verifies semantic memory and KB use the same runtime prompt, excluding stale KB content.
Helm chart tests
coding-agent/tests/deploy/test_helm_chart.py
New tests assert semantic_memory precedes kb in the rendered plugin list, default defer_when_semantic_memory_hits is False, and the flag renders as True when set.

Sequence Diagram(s)

sequenceDiagram
  participant Pipeline
  participant SemanticMemoryPlugin
  participant ctx.config
  participant KBPlugin
  participant KB

  Pipeline->>SemanticMemoryPlugin: build_context(ctx, tape)
  SemanticMemoryPlugin->>ctx.config: clear SEMANTIC_MEMORY_GROUNDING_MARKER_KEY
  SemanticMemoryPlugin->>SemanticMemoryPlugin: plan recall (lazy topic index)
  SemanticMemoryPlugin->>ctx.config: set marker {query_digest, tape_len, hit_count}
  Pipeline->>KBPlugin: build_context(ctx, tape)
  KBPlugin->>KBPlugin: extract query from runtime messages
  KBPlugin->>ctx.config: pop SEMANTIC_MEMORY_GROUNDING_MARKER_KEY
  alt hit_count > 0 and defer_when_semantic_memory_hits
    KBPlugin-->>Pipeline: return [] (skipped)
  else
    KBPlugin->>KB: search(query)
    KB-->>KBPlugin: results
    KBPlugin-->>Pipeline: context pack
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 Hoppity-hop through the memory lane,
Semantic hits mean KB can abstain!
A marker is set, a digest is hashed,
Stale README results are finally dashed.
The rabbit ensures the right prompt prevails—
Fresh context wins while old knowledge pales! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: aligning semantic memory and KB recall behavior.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/kb-defer-on-semantic-memory

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.

@delete-cloud
delete-cloud marked this pull request as ready for review June 30, 2026 02:52

@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: 1

🧹 Nitpick comments (2)
coding-agent/src/coding_agent/plugins/semantic_memory.py (1)

71-84: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Serialize lazy topic-index initialization.

_derived_topic_index is filled only after the await, so concurrent cold-start build_context() calls on the same plugin instance can each rebuild the full store-backed index before the cache is populated.

♻️ Proposed fix
+import asyncio
 import hashlib
 import inspect
 from collections.abc import Callable, Mapping
 from datetime import UTC, datetime
 from typing import Any
@@
         self._read_enabled = read_enabled
         self._limit = limit
         self._topic_store = _validate_topic_store(topic_store)
         self._topic_index = _validate_topic_index(topic_index)
         self._derived_topic_index: TopicRangeIndex | None = None
+        self._derived_topic_index_lock = asyncio.Lock()
@@
     async def _topic_index_for_context(self) -> TopicRangeIndex:
         if self._topic_index is not None:
             return self._topic_index
-        if self._derived_topic_index is None:
-            derived = await build_topic_range_index_from_store(self._topic_store)
-            self._derived_topic_index = (
-                TopicRangeIndex() if derived is None else derived.index
-            )
+        if self._derived_topic_index is not None:
+            return self._derived_topic_index
+        async with self._derived_topic_index_lock:
+            if self._derived_topic_index is None:
+                derived = await build_topic_range_index_from_store(self._topic_store)
+                self._derived_topic_index = (
+                    TopicRangeIndex() if derived is None else derived.index
+                )
         return self._derived_topic_index
🤖 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 `@coding-agent/src/coding_agent/plugins/semantic_memory.py` around lines 71 -
84, The lazy initialization in SemanticMemoryPlugin._topic_index_for_context is
not serialized, so concurrent cold-start build_context calls can all rebuild the
store-backed index before _derived_topic_index is set. Add an async guard around
the initialization path in _topic_index_for_context (using the
SemanticMemoryPlugin instance state) so only one
build_topic_range_index_from_store call runs and the rest await the cached
result. Keep the fast path for already-populated _topic_index and
_derived_topic_index unchanged.
coding-agent/src/coding_agent/plugins/kb.py (1)

30-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract runtime-query selection into a shared helper.

KBPlugin and SemanticMemoryPlugin now duplicate the same runtime-message filtering and payload-text extraction. Since the deferral contract depends on both plugins hashing/searching the exact same query, any drift here will silently reintroduce stale-query mismatches.

Also applies to: 141-143, 251-270

🤖 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 `@coding-agent/src/coding_agent/plugins/kb.py` around lines 30 - 35, The
runtime-query selection and text-extraction logic is duplicated between KBPlugin
and SemanticMemoryPlugin, so move the shared filtering and payload-to-query
conversion into a single helper and have both plugins call it. Use the existing
KBPlugin runtime message handling, SemanticMemoryPlugin search/hash path, and
the _RUNTIME_QUERY_KINDS set as the anchor points, and ensure both deferral and
search code paths use the exact same helper so the hashed query and searched
query always stay aligned.
🤖 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 `@coding-agent/tests/coding_agent/test_context_system_smoke.py`:
- Around line 36-41: Add a list_topics() method to FakeTopicStore so it matches
the topic_store contract expected by SemanticMemoryPlugin when it lazily builds
its TopicRangeIndex. Update the fake in test_context_system_smoke.py to mirror
the semantic-memory test fake: keep load_topic() behavior and also expose
list_topics() returning the stored TopicRecord values so build_context() can
construct the index without failing before the assertion.

---

Nitpick comments:
In `@coding-agent/src/coding_agent/plugins/kb.py`:
- Around line 30-35: The runtime-query selection and text-extraction logic is
duplicated between KBPlugin and SemanticMemoryPlugin, so move the shared
filtering and payload-to-query conversion into a single helper and have both
plugins call it. Use the existing KBPlugin runtime message handling,
SemanticMemoryPlugin search/hash path, and the _RUNTIME_QUERY_KINDS set as the
anchor points, and ensure both deferral and search code paths use the exact same
helper so the hashed query and searched query always stay aligned.

In `@coding-agent/src/coding_agent/plugins/semantic_memory.py`:
- Around line 71-84: The lazy initialization in
SemanticMemoryPlugin._topic_index_for_context is not serialized, so concurrent
cold-start build_context calls can all rebuild the store-backed index before
_derived_topic_index is set. Add an async guard around the initialization path
in _topic_index_for_context (using the SemanticMemoryPlugin instance state) so
only one build_topic_range_index_from_store call runs and the rest await the
cached result. Keep the fast path for already-populated _topic_index and
_derived_topic_index unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b9117dc-67b3-49a6-860d-e58e5a28305a

📥 Commits

Reviewing files that changed from the base of the PR and between 52cba5f and 36e1c36.

📒 Files selected for processing (11)
  • coding-agent/helm/templates/configmap-agent-config.yaml
  • coding-agent/helm/values.yaml
  • coding-agent/src/coding_agent/core/app.py
  • coding-agent/src/coding_agent/plugins/kb.py
  • coding-agent/src/coding_agent/plugins/semantic_memory.py
  • coding-agent/src/coding_agent/topics/range_index_builder.py
  • coding-agent/tests/coding_agent/plugins/test_kb_plugin.py
  • coding-agent/tests/coding_agent/plugins/test_semantic_memory.py
  • coding-agent/tests/coding_agent/test_bootstrap.py
  • coding-agent/tests/coding_agent/test_context_system_smoke.py
  • coding-agent/tests/deploy/test_helm_chart.py

Add list_topics to the smoke FakeTopicStore so the test double matches the lazy semantic topic-index contract.

Verification: uv run pytest tests/coding_agent/test_context_system_smoke.py tests/coding_agent/plugins/test_semantic_memory.py tests/coding_agent/plugins/test_kb_plugin.py -v

Verification: uv run ruff check tests/coding_agent/test_context_system_smoke.py && uv run ruff format --check tests/coding_agent/test_context_system_smoke.py
@delete-cloud
delete-cloud merged commit e4c8215 into main Jun 30, 2026
2 checks passed
@delete-cloud
delete-cloud deleted the feat/kb-defer-on-semantic-memory branch June 30, 2026 03:13
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