feat(agent-memory): Phase 4 — recall reinforcement#251
Conversation
dad22d0 to
a102ad0
Compare
56f9cf0 to
1859ed5
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1859ed5. Configure here.
1859ed5 to
8739aa6
Compare
a102ad0 to
3dc84bb
Compare
dcfde4b to
38a6515
Compare
KIvanow
left a comment
There was a problem hiding this comment.
One thing I'd like to sort before this merges.
Reinforcement doesn't actually influence recall ranking. reinforce() writes last_accessed_at, but the composite recency term still decays from createdAt (ageSeconds = (now - item.createdAt) / 1000 in recall()); lastAccessedAt is parsed onto the item but never read by the scoring path. So reinforcing a memory does not make it any more likely to be recalled next time, which is the feedback loop the name implies. As it stands the write is effectively inert for recall.
If the intent is that last_accessed_at only feeds Phase 5 eviction (not recall recency), that's fine, but it's worth saying so explicitly, because right now "reinforcement" reads as if it should boost future recall and it doesn't. If recency is meant to reflect access, the composite should use max(createdAt, lastAccessedAt) (or lastAccessedAt) for the age calculation. Either way, worth a one-line decision here so the behavior matches the name.
8ab239a to
96f5b18
Compare
38a6515 to
fc1be01
Compare
- Recalled items bump last_accessed_at + access_count by default (reinforce) - reinforce:false skips the writes - Reinforcement is best-effort: a failure never breaks the recall read path (mirrors agent-cache "stats failure must not break the cache")
…ment counts reinforce() bumped last_accessed_at, but recall recency decayed from created_at and never read it, so reinforcing a memory did nothing for its recall ranking. Recency now decays from max(created_at, last_accessed_at), making reinforcement the recall feedback loop the name implies.
96f5b18 to
a1c3786
Compare
fc1be01 to
e8e841c
Compare

Agent Memory — Phase 4: recall reinforcement
Stacked on #250 (Phase 3). Small phase.
What's new
recall()bumps each returned item'slast_accessed_at(HSET) andaccess_count(HINCRBY).reinforce: falseskips the writes..catch(() => undefined)) never breaks the recall read path, mirroring agent-cache's "stats failure must not break the cache."Tests
3 new unit tests (default-on bump, opt-out, failure-doesn't-break-recall); 30 total.
tsc --noEmit+ prettier clean.Next
Phase 5 (eviction & TTL —
selectEvictionspure selection + capacity/TTL writes).Note
Medium Risk
Changes recall ranking semantics and adds write side effects on every default recall, though failures cannot break reads and deleted keys are guarded.
Overview
Recall reinforcement (Phase 4): After ranking,
recall()by default updates each returned memory in Redis—last_accessed_atviaHSETandaccess_countviaHINCRBY. Callers can passreinforce: falseto skip writes. Reinforcement is best-effort (errors are swallowed) so the read path always returns hits. Before writing,EXISTSavoids touching keys that were already deleted but still appear in a stale search index.Ranking change: Recency in
compositeScorenow decays frommax(created_at, last_accessed_at)instead of creation time alone, so bumped access times actually improve future ranking. A new recall test asserts a recently accessed item beats an equally similar stale one when reinforcement is off.Reviewed by Cursor Bugbot for commit e8e841c. Bugbot is set up for automated code reviews on this repo. Configure here.