Description
Two related bugs in the user profile system:
- Profile items (preferences, patterns, workflows) accumulate indefinitely — automatic decay is ineffective and AI cleanup is never auto-triggered.
- AI cleanup with partial selection permanently deletes all unselected items.
Steps to Reproduce
Bug 1:
- Use opencode-mem for an extended period (hundreds of prompts)
- Check
user-profiles.db — item count only increases over time
- Changelogs across all versions show only additions, never removals
Bug 2:
- Open the opencode-mem web UI → User Profile page
- Select only a few items out of many
- Click "AI Cleanup" with partial selection
- Click "Apply"
- All unselected items are permanently deleted
Expected Behavior
Bug 1: Stale/low-confidence items should be automatically removed based on userProfileStaleDays and related config. AI cleanup should run periodically without manual intervention.
Bug 2: AI cleanup with partial selection should only modify selected items. Unselected items should be preserved.
Actual Behavior
Bug 1: decayItems() only removes items where alpha <= 2 && ageDays > 30 (hardcoded, user-profile-manager.js L346). Since alpha increments on every merge (combined.alpha += other.alpha || 1 L646, combined.alpha = (existingItem.alpha || 1) + top1Score * 1.0 L738), most items surpass alpha > 2 after 2-3 merges and can never be removed. Of the three config options, userProfileStaleDays and userProfileMinEvidenceForRetention are loaded but never referenced anywhere in the service code; userProfileConfidenceDecayDays IS referenced, but only by syncConfidence() (L1036) for confidence half-life decay — not for item removal decisions. AI cleanup (aiCleanupProfile) is only available via manual HTTP API call (api-handlers.js L874/L877), never triggered during profile learning (performUserProfileLearning() in user-memory-learning.js has no cleanup call).
Bug 2: handleAICleanup (api-handlers.js L855) with non-empty includeIds calls filterProfileForCleanup() (L872) which filters the profile down to only selected items, then aiCleanupProfileFromIndexed() builds originalById from that filtered set. rebuildProfileUsing() (user-profile/ai-cleanup.js L270) preserves unmentioned items via unmentionedIds (L397-429), but that set is derived only from originalById.values() — so unselected items (never in the indexed set) are invisible to the preservation logic. handleApplyCleanup() (L901) then replaces the entire profile with the cleaned subset (L1002), permanently losing every unselected item.
Location
Bug 1:
- File:
dist/services/user-profile/user-profile-manager.js
- Function:
decayItems() — Lines 330-357 (hardcoded check at L346)
- Also:
dist/services/user-memory-learning.js — performUserProfileLearning() never calls aiCleanupProfile()
Bug 2:
- File:
dist/services/user-profile/ai-cleanup.js (note: under user-profile/, not services/ root)
- Functions:
filterProfileForCleanup() (L70-78), rebuildProfileUsing() (L270-431)
- Also:
dist/services/api-handlers.js (note: under services/ root, not web-server/)
- Functions:
handleAICleanup() (L855), handleApplyCleanup() (L901)
Impact
Bug 1: Profile grows unbounded over time, degrading AI context quality and increasing token cost.
Bug 2: Data loss — users lose their entire profile history when trying to clean up a small subset of items. No undo.
Suggested Fixes
Bug 1: Use config values in decay logic instead of hardcoded constants. Auto-trigger AI cleanup periodically during profile learning.
Bug 2: Pass full profileData to rebuildProfileUsing() as originalById, while only sending selected items to AI. This lets unmentionedIds correctly preserve unselected items (verified: the unmentionedIds loop at L402-429 walks originalById.values() and would retain every id not in keptIds/mapping.removed — with the full profile as originalById, unselected items fall into this set and are preserved).
Description
Two related bugs in the user profile system:
Steps to Reproduce
Bug 1:
user-profiles.db— item count only increases over timeBug 2:
Expected Behavior
Bug 1: Stale/low-confidence items should be automatically removed based on
userProfileStaleDaysand related config. AI cleanup should run periodically without manual intervention.Bug 2: AI cleanup with partial selection should only modify selected items. Unselected items should be preserved.
Actual Behavior
Bug 1:
decayItems()only removes items wherealpha <= 2 && ageDays > 30(hardcoded,user-profile-manager.jsL346). Since alpha increments on every merge (combined.alpha += other.alpha || 1L646,combined.alpha = (existingItem.alpha || 1) + top1Score * 1.0L738), most items surpass alpha > 2 after 2-3 merges and can never be removed. Of the three config options,userProfileStaleDaysanduserProfileMinEvidenceForRetentionare loaded but never referenced anywhere in the service code;userProfileConfidenceDecayDaysIS referenced, but only bysyncConfidence()(L1036) for confidence half-life decay — not for item removal decisions. AI cleanup (aiCleanupProfile) is only available via manual HTTP API call (api-handlers.jsL874/L877), never triggered during profile learning (performUserProfileLearning()inuser-memory-learning.jshas no cleanup call).Bug 2:
handleAICleanup(api-handlers.jsL855) with non-emptyincludeIdscallsfilterProfileForCleanup()(L872) which filters the profile down to only selected items, thenaiCleanupProfileFromIndexed()buildsoriginalByIdfrom that filtered set.rebuildProfileUsing()(user-profile/ai-cleanup.jsL270) preserves unmentioned items viaunmentionedIds(L397-429), but that set is derived only fromoriginalById.values()— so unselected items (never in the indexed set) are invisible to the preservation logic.handleApplyCleanup()(L901) then replaces the entire profile with the cleaned subset (L1002), permanently losing every unselected item.Location
Bug 1:
dist/services/user-profile/user-profile-manager.jsdecayItems()— Lines 330-357 (hardcoded check at L346)dist/services/user-memory-learning.js—performUserProfileLearning()never callsaiCleanupProfile()Bug 2:
dist/services/user-profile/ai-cleanup.js(note: underuser-profile/, notservices/root)filterProfileForCleanup()(L70-78),rebuildProfileUsing()(L270-431)dist/services/api-handlers.js(note: underservices/root, notweb-server/)handleAICleanup()(L855),handleApplyCleanup()(L901)Impact
Bug 1: Profile grows unbounded over time, degrading AI context quality and increasing token cost.
Bug 2: Data loss — users lose their entire profile history when trying to clean up a small subset of items. No undo.
Suggested Fixes
Bug 1: Use config values in decay logic instead of hardcoded constants. Auto-trigger AI cleanup periodically during profile learning.
Bug 2: Pass full
profileDatatorebuildProfileUsing()asoriginalById, while only sending selected items to AI. This letsunmentionedIdscorrectly preserve unselected items (verified: theunmentionedIdsloop at L402-429 walksoriginalById.values()and would retain every id not inkeptIds/mapping.removed— with the full profile asoriginalById, unselected items fall into this set and are preserved).