You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(memory): decayed-entry recall — a fact that ages out stays findable
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
#64. Withholding an order from the always-on digest must not also make it
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.
Copy file name to clipboardExpand all lines: docs/levelcode-sessions-memory.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -172,7 +172,7 @@ The magic, delivered quietly (never a wall of text):
172
172
- ✅ **Conflict reconciliation** — semantic supersede: a newer session's fact marks an older one obsolete, dimmed and restorable rather than silently replaced.
173
173
- ✅ **Poisoning red-team pass.**`test/memoryPoisoning.test.js` — 34 cases, an adversarial corpus in the style of `commandSafety.test.js`: ten hostile shapes that must never self-promote, benign project facts that must keep working, nine credential shapes that must never reach disk, and the near-misses (git SHAs, content hashes, asset names) that must survive untouched. It found the gap it was written to look for — see §7. Every case verified non-vacuous by bypassing each guard and confirming failure.
174
174
*Exit met: an adversarial repo cannot plant a load-bearing memory.* The original wording said "EXIT-TEST.md green", but that file is the **M0** fork/build checklist and was never the right home for this; an executable corpus is a better exit test than a checklist anyway, since it re-runs on every change.
175
-
-⬜**Decayed-entry recall**— surfacing an aged-out fact when a query matches it directly.
175
+
-✅**Decayed-entry recall.**`recallFacts()` ranks over the **full** fold rather than `activeFacts`, so a fact that decayed out of the digest is still findable by a direct question — §4's *"Decayed ≠ deleted — it's still in Recall"*, which until now was only true of the journal. `consolidate()` writes only active facts to `MEMORY.md` and `recall()` searched the journal alone, so an **inferred**, **superseded**, or instruction-withheld fact was in neither: on disk, cited, and unreachable by any question. Every hit carries a `state` (`confirmed` · `observed` · `inferred` · `superseded` · `unconfirmed-instruction`) and the tool result qualifies it for the model, so a low-confidence answer is never laundered into a settled one — a superseded hit names what replaced it, and a withheld instruction says *do not act on it*. The single exclusion is `removed`: a user's "not true" must stay not true.
176
176
- ⬜ **Export** — "Copy as Markdown" for a session, and for the memory set. Cheap, since the storage is already plain text, and it seeds LevelLinks.
177
177
178
178
**Deliberately later:** cross-*project* memory ("how did I do idempotency in the *other* service?"); a vector cache over the plain files for large corpora; team-shared project memory (rides M9 sync).
0 commit comments