Skip to content

Commit b0fb111

Browse files
authored
fix(claude-code): skip primary + duplicate banks in recallAdditionalBanks (#2625)
The additional-banks recall loop recalled every entry with no dedup against the resolved primary, so bidirectional cross-bank setups (primary listed in recallAdditionalBanks) re-recalled the primary on every prompt — a wasted recall call plus duplicate context. Guard the loop with a seen-set seeded with the primary bank; also dedups repeated entries. Fixes #2604.
1 parent 4bf126b commit b0fb111

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

hindsight-integrations/claude-code/scripts/recall.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,15 @@ def _dbg(*a):
201201

202202
results = response.get("results", [])
203203

204-
# Also recall from any additional banks (e.g. shared user profile bank)
204+
# Also recall from any additional banks (e.g. shared user profile bank).
205+
# Skip the primary (already recalled above) and any repeated entry so
206+
# bidirectional cross-bank setups don't re-recall a bank they already hit.
205207
additional_banks = config.get("recallAdditionalBanks", [])
208+
seen_banks = {bank_id}
206209
for extra_bank_id in additional_banks:
210+
if extra_bank_id in seen_banks:
211+
continue
212+
seen_banks.add(extra_bank_id)
207213
extra_filter = additional_bank_filters.get(extra_bank_id, {})
208214
extra_tags = extra_filter.get("recallTags", recall_tags) or None
209215
extra_tag_groups = extra_filter.get("recallTagGroups", tag_groups) or None

hindsight-integrations/claude-code/tests/test_hooks.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,34 @@ def capture_and_respond(req, timeout=None):
275275
assert captured[1]["tags"] == ["memory_type:rule"]
276276
assert captured[1]["tags_match"] == "all_strict"
277277

278+
def test_additional_banks_skip_primary_and_duplicates(self, monkeypatch, tmp_path):
279+
"""The primary bank (and repeated entries) must not be re-recalled."""
280+
recalled_banks = []
281+
282+
def capture_and_respond(req, timeout=None):
283+
if "/recall" in req.full_url:
284+
recalled_banks.append(req.full_url)
285+
return FakeHTTPResponse({"results": []})
286+
287+
hook_input = make_hook_input(prompt="anything")
288+
_run_hook(
289+
"recall",
290+
hook_input,
291+
monkeypatch,
292+
tmp_path,
293+
urlopen_side_effect=capture_and_respond,
294+
extra_settings={
295+
"bankId": "shared-bank",
296+
# primary listed for bidirectional visibility, plus a dup + a real extra
297+
"recallAdditionalBanks": ["shared-bank", "other-bank", "other-bank"],
298+
},
299+
)
300+
301+
# shared-bank recalled once (primary), other-bank once — 2 calls, not 4.
302+
assert sum("shared-bank" in url for url in recalled_banks) == 1
303+
assert sum("other-bank" in url for url in recalled_banks) == 1
304+
assert len(recalled_banks) == 2
305+
278306
def test_disabled_auto_recall_produces_no_output(self, monkeypatch, tmp_path):
279307
(tmp_path / "plugin_root").mkdir(exist_ok=True)
280308
(tmp_path / "plugin_data").mkdir(exist_ok=True)

0 commit comments

Comments
 (0)