fix: stamp beliefs.last_retrieved_at on hook-driven retrieval (#222) - #266
Merged
Conversation
The retrieval audit loop was structurally half-implemented: the UserPromptSubmit hook wrote one feedback_history row per retrieved belief but never stamped beliefs.last_retrieved_at. Result on the canonical store: 0 of 20,852 beliefs populated, despite 586 feedback_history rows tagged source='hook' from a single day. Grep across src/aelfrice/ confirmed no code path issued \`UPDATE beliefs SET last_retrieved_at = ?\` outside update_belief()'s full-row rewrite (which fires on belief edits, not retrieval). All INSERT sites hard-coded the column to None. Add MemoryStore.stamp_retrieved(belief_ids, ts=None): single batched UPDATE, defaults ts to UTC now, returns rowcount, no-op on empty. Wire it into hook_search.record_retrieval after the apply_feedback loop, stamping only the ids whose feedback_history write actually succeeded — failed writes don't get the recency mirror. Tests: - store_crud: populate, explicit ts, empty, missing-id, overwrite. - hook_search: stamp on retrieval, skip-on-failure, no-op on empty, search_for_prompt end-to-end stamping. Closes #222.
This was referenced Apr 29, 2026
robotrocketscience
added a commit
that referenced
this pull request
Apr 29, 2026
## Summary Spec memo for #289. Phase-2a of the #286 redesign — the gating contract change so `rebuild_v14()` can return empty when no candidate clears the floor. **Doc-only — no code change. Posted for ratification.** Five recommendations: 1. **Yes — add a silent path.** All-floored-out → `""`. No empty-marker tag. 2. **Composite score formula.** `bm25_normalized * (0.5 + 0.5 * posterior_mean)`. Posterior-mean weighting prevents stale-but-token-overlapping beliefs (the `1bc8ab45a40351d9` example in #281) from passing. 3. **Three-tier per-lane floors.** L0 locked = no floor (operator intent dominates). L2 session-scoped = soft floor `T_session = 0.10`. L1 / L2.5 = hard floor `T_l1 = 0.40`. 4. **No `last_retrieved_at` stamp on floored-out hits.** Stamp moves to *after* floor application; floored-out beliefs never reach the stamp loop. Protects the hibernation staleness signal (#196). 5. **Ship with placeholder T defaults; calibrate from #288 logs.** Floor *value* blocks on #288 eval harness data; floor *shape* lands in v1.x with operator-tunable `[rebuild_floor]` config. ## Decision asks (five) Bottom of the memo. Composite formula, per-lane structure, empty-path shape, stamp ownership, calibration plan. ## What ships if ratified One PR (~400 lines): - `rebuild_v14` composite + floor + empty-path return - `hook.py` precompact-envelope drop on empty - `hook_search.py` stamp move (only survivors get stamped) - `[rebuild_floor]` config block + tests ## Test plan - [x] Memo cross-references #286, #288, #281, #222/#266 - [x] No code change; nothing to test - [ ] Ratification or override comment from maintainer Refs #289, #286
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.
Summary
Closes #222. The retrieval audit loop was structurally half-implemented — the hook wrote
feedback_historyrows but never stampedbeliefs.last_retrieved_at. Verified live: 0 of 20,852 beliefs populated on canonical store despite 586 hook-tagged feedback rows from one day.Root cause: no code path anywhere in
src/aelfrice/issuedUPDATE beliefs SET last_retrieved_at = ?outsideupdate_belief()'s full-row rewrite (which fires on belief edits, not retrieval). All INSERT sites hard-codedNone.Changes
MemoryStore.stamp_retrieved(belief_ids, ts=None)— single batched UPDATE; defaults ts to UTC now; returns rowcount; no-op on empty.hook_search.record_retrieval— after theapply_feedbackloop, callsstamp_retrievedwith only the ids whose feedback write succeeded. Same best-effort posture as the audit loop (errors logged to stderr, never block).Test plan
tests/test_store_crud.py— 5 new: populate, explicit ts, empty input, missing id, overwrite prior ts.tests/test_hook_search.py— 4 new: stamp on retrieval, skip-on-failure, no-op on empty, search_for_prompt end-to-end.test_serve_raises_clear_error_when_fastmcp_missing— environment-dependent, fails onmainHEAD too).Out of scope
No backfill of historic rows. The 20,852 already-
NULLbeliefs stayNULL— column is now populated forward from this commit. If a backfill is wanted, a separateaelf migrate --backfill-retrievalcould derive timestamps fromfeedback_history.created_at WHERE source='hook'for the 413 belief ids that have any hook-tagged event. Filing a follow-up issue if the user wants this.