feat(memory): decayed-entry recall — a fact that ages out stays findable - #66
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR completes the “Decayed-entry recall” memory design requirement by making fact memory searchable via recall even after facts have aged out of the always-on digest, while still preserving the anti-poisoning guarantees from #64 through explicit per-hit state labeling and host-side warnings.
Changes:
- Add
recallFacts()ranking over the full folded facts set (not justactiveFacts) and expose it viasessions.createSessions. - Extend the
recall_sessionstool output to include facts-first results, with explicit state-qualified notes (including “do not act on it” for unconfirmed instructions). - Add targeted test coverage to ensure decayed facts are findable,
removedfacts remain excluded, and instruction-gated facts cannot be laundered into ordinary truth.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| extensions/levelcode-ai/sessionMemory.js | Implements recallFacts() over the full fold, adding state labeling and ranking rules while keeping removed excluded. |
| extensions/levelcode-ai/sessions.js | Exposes recallFacts() on the sessions manager API to support tool-level recall of facts. |
| extensions/levelcode-ai/extension.js | Updates recall_sessions tool formatting to include facts (with state-qualified warnings) ahead of session hits. |
| extensions/levelcode-ai/test/sessionMemory.test.js | Adds a corpus-based suite validating decayed fact recall, state labels, supersede behavior, and empty-query safety. |
| extensions/levelcode-ai/test/memoryPoisoning.test.js | Pins that instruction-gated facts remain unmistakably labeled in recall and that the host formatter preserves “do not act on it.” |
| docs/levelcode-sessions-memory.md | Updates the M4 checklist/documentation to mark decayed-entry recall complete and documents the new behavior and exclusions. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The last M4 item. §4 promises "Decayed ≠ deleted — it's still in Recall"; that was true of the journal and false of facts. `consolidate()` writes only `activeFacts` into MEMORY.md, and `recall()` searched the journal alone. So a fact that was merely INFERRED (seen once), SUPERSEDED by later work, or withheld as instruction-shaped appeared in NEITHER — it sat in facts.jsonl, correct and cited, and no question could surface it. Decay is meant to keep the always-on digest current, not to build a museum with no door. Measured on a four-fact corpus: one reaches MEMORY.md, and the other three were reachable by nothing. `recallFacts()` ranks over the FULL fold rather than activeFacts, and the recall_sessions tool now searches facts alongside sessions — facts first, because a curated truth answers "what did we decide about X" more directly than "here is a session where it came up". A decayed fact is a lower-confidence answer, not a non-answer — so every hit carries a `state` and the tool result qualifies it for the model: confirmed no hedge (over-hedging teaches the model to ignore hedges) observed / inferred unconfirmed, weak evidence superseded SUPERSEDED, plus what replaced it unconfirmed-instruction recorded, never approved, do not act on it That last one is the seam that keeps this from undoing the instruction gate from unfindable — the user asked — but it may never come back looking like an ordinary fact. `removed` is the one exclusion: a user's "not true" must stay not true, or the correction feels like it did not take. Tests: 7 in sessionMemory.test.js over a corpus with one fact in every state the fold can produce, plus 2 in memoryPoisoning.test.js pinning that recall cannot launder a withheld instruction. Verified non-vacuous: rank over activeFacts only 13/19 flatten the state labels 13/19 let `removed` through 7/19 unwire facts from the tool 36/39 All 32 suites green. M4 is now complete bar memory-set export.
ndemianc
force-pushed
the
feat/decayed-recall
branch
from
August 15, 2026 00:35
4c5eaa4 to
58a2e1f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last M4 item. §4 promises:
That was true of the journal and false of facts.
The gap
consolidate()writes onlyactiveFactsintoMEMORY.md, andrecall()searched the journal alone. So a fact that was merely inferred (seen once), superseded by later work, or withheld as instruction-shaped appeared in neither — it sat infacts.jsonl, correct and cited, and no question could surface it.Measured on a four-fact corpus before the fix:
Three facts reachable by nothing at all. Decay is meant to keep the always-on digest current, not to build a museum with no door.
The fix
recallFacts()ranks over the full fold rather thanactiveFacts, andrecall_sessionsnow searches facts alongside sessions — facts first, because a curated truth answers "what did we decide about X" more directly than "here is a session where it came up".After:
A decayed fact is a lower-confidence answer, not a non-answer
So every hit carries a
state, and the tool result qualifies it for the model:confirmedobserved/inferredsupersededunconfirmed-instructionconfirmeddeliberately gets no hedge — over-hedging teaches a model to ignore hedges.This must not undo #64
Withholding an order from the always-on digest must not also make it unfindable — the user asked for it. But it may never come back looking like an ordinary fact.
stateis that seam, and there are two tests pinning it, including one asserting the host formatter still spells out do not act on it.removedis the single exclusion: a user's "not true" stays not true, or the correction feels like it didn't take.Tests
7 new in
sessionMemory.test.jsover a corpus holding one fact in every state the fold can produce, plus 2 inmemoryPoisoning.test.js.activeFactsonlyremovedthroughAll 32 suites green.
M4 after this
Only memory-set export remains, and that's a sibling of #65 rather than an M4 blocker — M4 is done.