danielmiessler / danielmiessler/LifeOS
memory-retrievals.jsonl is read by three health checks and written by nothing, so retrieval freshness fails permanently on a healthy install
- Dominant language
- TypeScript
- Stars
- 19k
- Forks
- 2.5k
- Avg merge
- 8d 17h
- Merged PRs (30d)
- 1
Description
# `memory-retrievals.jsonl` is read by three health checks and written by nothing, so retrieval freshness fails permanently on a healthy install
**Version:** LifeOS 7.40.4
## The bug
`CortexHealth.ts:206`, `MemoryStatus.ts:44` and `MemoryHealthCheck` all read
`LIFEOS/MEMORY/OBSERVABILITY/memory-retrievals.jsonl` and grade retrieval
freshness from its most recent row. `MemoryRetriever.ts` — the code that actually
performs retrieval — never writes it.
```
$ grep -rl memory-retrievals LIFEOS/TOOLS hooks
LIFEOS/TOOLS/CortexHealth.ts
LIFEOS/TOOLS/MemoryStatus.ts
```
Two readers, zero writers. `grep -niE 'appendFile|writeFile|jsonl' LIFEOS/TOOLS/MemoryRetriever.ts`
returns only `console.log` calls — the retriever prints, it never records.
Result, on an otherwise healthy install:
```
warn - No retrieval evidence within 86400000ms freshness window.
```
reported forever, unconditionally, while retrieval runs on every single turn and
injects `` correctly. The warning is not describing a real
degradation; the file has simply never existed.
## Repro
1. Fresh 7.40.4 install, run any session so `MemoryTurnStart` fires (it calls
`getRelevantContext`, so retrieval genuinely happens and its output is visible
in the injected `` block).
2. `ls $LIFEOS_DIR/MEMORY/OBSERVABILITY/memory-retrievals.jsonl` → does not exist.
3. `bun $LIFEOS_DIR/TOOLS/MemoryHealthCheck.ts` → `retrieval-missing` warning,
and it never clears no matter how much retrieval occurs.
## Fix applied locally, and one gotcha for upstream
Made `getRelevantContext` a thin wrapper that times the real call and appends one
row. Worth knowing before implementing: the row shape is strictly constrained by
`CortexHealth.validRetrievalRow`, which uses `hasExactKeys` — exactly `ts`,
`query_hash`, `returned_count`, `duration_ms`, plus optional `top_score`, and **no
other keys**. An extra field (a `query` field, say) grades the row `invalid`
rather than `ok`, which would swap one wrong warning for another.
Hashing rather than storing the query looks deliberate and right — these are the
user's own prompts, and an observability log is the wrong home for them. Worth a
comment in the code saying so, since the obvious "improvement" is to log the query
text.
After the fix, `MemoryHealthCheck` reports `overall: ok` with the retrieval row
read as `status: "ok"`.
## Two smaller things found alongside
**Reviewer failures are labelled "(error/parse)" when nothing failed to parse.**
`MemoryHealthCheck` reports `N of last 5 reviewer runs failed (error/parse)`. In my
case every failure was an `EWRITE_FAILED` boundary refusal occurring *after*
successful inference, with `parse_ok: true` on every run including the failures.
The label sent me looking for malformed model output for a while. Naming the actual
dispatch failure would be clearer.
**Substantive positive feedback is not captured, only terse praise.**
`SatisfactionCapture`'s praise fast-path (around line 450) fires on standalone
praise words and short phrases, recording a rating with a `Direct praise:` summary.
That works. But considered positive feedback embedded in a longer message does not
match it. Concretely: the user told me that a particular habit — answering
library-behaviour questions by writing a minimal runnable test rather than
recalling from memory — was load-bearing for his trust in the answers. That is
exactly the kind of "keep doing this" worth promoting to a durable rule, and
nothing captured it, because it arrived inside a paragraph rather than as "nice".
Suggested framing: the implicit sentiment path already writes narrative analyses
for negative exchanges. Letting it emit a positive class with "what earned it"
would make the loop two-sided. As it stands, a rule-mining pass over the corpus can
only ever propose things to stop doing.
## Note on scope
I originally drafted this issue with a third claim: that the explicit-rating
detector misreads terse numbered replies ("yes delete, 2 yes rename to xxx") as
literal 1-2/10 scores. That is real in my historical corpus — 39 of 41
`low-rating-*` files are false positives — but **it is already fixed in current
code**. `parseExplicitRating` now rejects all five strings I tested that had
produced false captures, while still accepting `8`, `2/10` and `8 nice`, and the
false positives stop dead: 19 in May, 3 in June, 1 in July, none since. The guard
comments in the source describe exactly this case. Dropped it rather than report a
solved problem; recording it here only so the corpus artifact is not mistaken for a
live bug by anyone else mining old captures.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with MemoryRetriever.ts and its getRelevantContext path, then compare the recorded row with CortexHealth.validRetrievalRow and the readers in CortexHealth.ts and MemoryStatus.ts. Run MemoryHealthCheck.ts after a retrieval on a fresh install; done means retrieval-missing clears and the row is status "ok" with only the permitted keys.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100