fix: include social tier in deletion paths (#291) - #308
Conversation
prakashUXtech
left a comment
There was a problem hiding this comment.
This is the strongest fail-without-fix evidence in the batch — 10 of your new tests fail on dev with correct root causes, including a real save → awaken → forget → re-awaken round-trip that asserts on persisted retrieval weight. And _all_stores() is the right shape: one list, twelve call sites, so a fifth tier is picked up everywhere automatically. Tracing it back to when SocialStore was added was good diagnosis.
Three things before merge, and the first is structural.
1. The branch is mis-stacked. fix/issue-291-social-deletion was cut from docs/drift-290, not from dev, but declares base: dev. Commit a62eff1 in this PR is byte-identical to #307's head — so merging #308 would land #290's documentation under the #291 title and leave #307 with nothing. Rebase onto origin/dev and drop a62eff1. (This is the third PR in this batch stacked on an unmerged sibling without declaring it — worth making a habit of git log --oneline origin/dev..HEAD before opening.)
2. The operator-facing surfaces still under-report social — the same false-assurance the issue is about, moved up a layer. soul forget --entity Alice now genuinely deletes social entries, but _tier_counts in cli/main.py omits social from the printed breakdown, so the operator sees nothing in social and concludes nothing was there. Same in the MCP forget tool's tiers, and forgetting a social id displays total: 1 with all three tier lists empty. If the point is that operators shouldn't wrongly believe data is gone, they equally shouldn't wrongly believe it was never there.
3. Soul._memory_lookup_sync still skips social — and its docstring says "Synchronous lookup across all built-in stores". That's the exact bug #291 names, in a function whose docstring now actively lies. It's what the provenance walker uses to resolve a cursor, so a chain walk silently stops at a social entry.
Two regressions the PR introduces:
soul status Total no longer adds up. count() now includes social, and it feeds memory_count, but the status panel prints only three tier rows. So you get Episodic 4 / Semantic 7 / Procedural 2 and then "Total 16" with five social memories invisible. Same shape in demo.py. count() wasn't in #291's scope — either add the Social row or revert that change.
count() re-arms the bug inside the helper meant to prevent it. It iterates _all_stores() and then throws the iteration away:
for _name, store in self._all_stores():
if _name == "episodic":
total += len(self._episodic._memories)
elif _name == "semantic":
...store is bound and never used, so a fifth tier added to _all_stores() falls through every branch and counts as zero, silently. SocialStore.count() exists — total += store.count() is what the surrounding comment already tells you to do.
Then: a CHANGELOG entry (this changes the forget* return shape — additive, but declare it); the soul.py header comment; v0.6.0 in two comments should be v0.5.0; four stale doc lines in api-reference.md and cli-reference.md that still list the three-tier result shape and supersede --type choices; and tests for update/supersede on a social id, since the issue named those and only purge is covered.
One for a follow-up issue: _all_stores() covers the four built-ins but not custom layers or the archival store, and BondRegistry still holds Alice's bond record after forget_entity("Alice") — strength, interaction count, bonded-at, persisted to the .soul. Until those close, "GDPR-complete" is overclaiming; worth a .. note:: in the docstring naming what isn't covered.
The core fix is genuinely correct — these are mechanical.
_forget_bulk(), forget(), forget_entity(), forget_before(), remove(), _find_entry_by_id(), forget_by_id(), purge_by_id(), update_in_place(), set_retrieval_weight(), supersede(), and count() now all iterate over the social memory tier alongside episodic/semantic/procedural. A new _all_stores() helper centralizes tier iteration so future tiers are never silently skipped again. Closes qbtrix#291
… count(), docs, tests (qbtrix#291) - Rebase onto upstream/dev, drop stacked qbtrix#290 docs commit - Add social to _tier_counts, forget --id reshape, soul status panel - Fix _memory_lookup_sync to include social (provenance walker) - Fix count() to use generic store.count() (add count() to all stores) - Add social to MCP soul_forget/soul_health tiers - Add social to supersede --type CLI choice - Update stale 3-tier doc lines in api-reference.md and cli-reference.md - Add CHANGELOG entry for qbtrix#291 (additive: social key in forget return) - Add GDPR coverage-gap docstring to forget_entity - Add tests: update/supersede on social IDs, _memory_lookup_sync social
67fba46 to
a10b4b5
Compare
prakashUXtech
left a comment
There was a problem hiding this comment.
Good pass — the mis-stacking is gone (the diff is now three commits, all #291), and most of the list is genuinely fixed. Confirmed: _memory_lookup_sync covers social with a test; all three operator surfaces now report it (_tier_counts, the forget --id reshape, and the MCP tool's tiers), plus supersede --type gained social as a bonus; count() is now a clean sum(store.count() for ...) so a fifth tier can't silently count zero; there are tests for update/supersede on a social id; and the CHANGELOG entry landed in the right section. I also mutation-tested the core: removing ("social", self._social) from _all_stores() turns 9 tests red, so the fix is genuinely pinned.
One correction to my own last review: the tier-table-with-Total panel is in soul inspect, not soul status. You fixed inspect correctly — my pointer was wrong.
Five small things left, and two of them are new:
New — MCP soul_health is now internally inconsistent. mcp/server.py:1564 still computes total = len(episodic) + len(semantic) + len(procedural) while the tiers dict at :1612 now reports social. A consumer summing the tiers won't get total. That's the same bug class you just fixed in inspect, re-created one file over.
New — the doc fix outran the code. docs/cli-reference.md:1610 now says soul health shows "episodic, semantic, procedural, social", but health_cmd still reads and prints three tiers. That's drift in the opposite direction, which is a bit ironic in this PR. I'd fix the code side rather than walk the doc back, since the MCP tool already reports social.
Then: demo.py:349-360 still lists three tier rows against a Total that now includes social; soul.py's header comment has no #291 entry; and v0.6.0 should be v0.5.0 in two comments (manager.py:1336, soul.py:2715) since the package is 0.5.0.dev0.
Two more worth knowing. Your forget_entity() reshape moved edges_removed to the front of the return dict — harmless for dict access, but it'll shift any golden JSON snapshot, and it isn't in the CHANGELOG. And health analytics on both surfaces still build all_mems from three tiers, so duplicate/low-importance/orphan detection never sees social entries — pre-existing rather than yours, but the same tier-blindness and worth a follow-up issue.
Cheap addition: nothing currently pins soul.memory_count including a social entry, which is exactly what regressed. One assertion would cover it.
Problem
forgetandforget_entityskip the social memory tier (#291).All deletion paths iterate a hardcoded episodic → semantic → procedural
triad, ignoring social memories entirely.
Root Cause
When SocialStore was added in PR #41, the layer dispatch system
(_store_in_layer, _delete_in_layer, etc.) and serialization were wired
correctly, but 11 deletion/mutation functions in MemoryManager plus
Soul._forget_bulk() were never updated to include the social tier.
Fix
MemoryManager._all_stores()— a single helper returningall four built-in stores as
(name, store)tuples.forget(),forget_entity(),forget_before(),remove(),_find_entry_by_id(),forget_by_id(),purge_by_id(),count(),update_in_place(),set_retrieval_weight(), andsupersede()touse the helper instead of hardcoded triad iteration.
Soul._forget_bulk()(v0.5.0 weight-decay path) to includesocial.
Files Changed
src/soul_protocol/runtime/memory/manager.py— primary fix + _all_stores() helpersrc/soul_protocol/runtime/soul.py— fix _forget_bulk() weight-decay pathtests/test_gdpr_deletion.py— 10 new tests + 2 updated assertionsTesting
_find_entry_by_idfinds social,forget/forget_entity/forget_before/purge_by_idall operate on social memories, round-trip persistence (save → awaken → forget → re-awaken), and audit trail coverage.Risk
Low. The fix is additive. The
_all_stores()helper is 4 lines. No publicAPI changes. No
RecallEnginechanges.Closes #291