Summary
On the Web UI page, the memory count in the header (top-right stats) does not update when filtering memories by tag. It always shows the global total from /api/stats regardless of the selected tag, so the number never matches the tag-filtered list shown below it.
The tag-filtered list itself is correct - only the count is wrong/stale.
Steps to reproduce
- Open the Web UI (
http://127.0.0.1:4747).
- Note the header total, e.g.
Total: 100.
- Select a tag from the tag filter dropdown that matches only a few memories (e.g. a tag with 3 memories).
- Observe: the list now shows only the 3 matching memories, but the header still shows
Total: 100.
- Switch to a different tag - the header number never changes.
Expected behavior
When a tag filter is active, the header count should reflect the filtered result set (the number of memories matching the selected tag) and update when switching tags.
Actual behavior
The header count is always the global /api/stats total. It only changes when a search query is entered, because getDisplayedMemoryCount returns the search total only when isSearching === true. A tag filter is not treated as "searching", so the store total is always shown.
Root cause analysis
The header count is wired to the wrong data source:
web/src/lib/memory-count.ts (L1-7) - getDisplayedMemoryCount(isSearching, searchTotal, storeTotal) returns storeTotal whenever not searching: return isSearching ? searchTotal : storeTotal;
web/src/App.tsx (L144-157) - header renders getDisplayedMemoryCount(explorer.isSearching, explorer.totalItems, explorer.statsTotal).
web/src/hooks/useMemoriesExplorer.ts - loadStats() (L55-60) fetches /api/stats with no tag parameter; onTagFilterChange() (L193-205) reloads the list (loadMemories) but never reloads stats.
src/services/api-handlers.ts - handleStats() (L719-756) returns global totals across all project + user shards with no tag awareness: total = userCount + projectCount.
handleListMemories() (L150-269) does return a correct filtered total (timeline.length) - but the frontend only uses totalItems when isSearching === true, so the tag-filtered count is discarded.
Secondary inconsistency: handleListTags() (L111-148) only returns project-scoped tags (filters on _project_), while handleStats counts both user and project memories. The displayed "total" can therefore be larger than anything reachable through the tag filter (user-scope memories never appear in the tag dropdown).
Suggested fix direction
Either:
- Make the header count tag-aware: pass the selected tag to
/api/stats (or return per-tag counts from handleListTags), and display the filtered count when a tag is active; or
- Change
getDisplayedMemoryCount to return totalItems when a tag filter is active, not only when searching: return isSearching || tagFilterActive ? searchTotal : storeTotal;
Environment
- OS: Windows
- opencode-mem: latest (npm v2.25.0)
Summary
On the Web UI page, the memory count in the header (top-right stats) does not update when filtering memories by tag. It always shows the global total from
/api/statsregardless of the selected tag, so the number never matches the tag-filtered list shown below it.The tag-filtered list itself is correct - only the count is wrong/stale.
Steps to reproduce
http://127.0.0.1:4747).Total: 100.Total: 100.Expected behavior
When a tag filter is active, the header count should reflect the filtered result set (the number of memories matching the selected tag) and update when switching tags.
Actual behavior
The header count is always the global
/api/statstotal. It only changes when a search query is entered, becausegetDisplayedMemoryCountreturns the search total only whenisSearching === true. A tag filter is not treated as "searching", so the store total is always shown.Root cause analysis
The header count is wired to the wrong data source:
web/src/lib/memory-count.ts(L1-7) -getDisplayedMemoryCount(isSearching, searchTotal, storeTotal)returnsstoreTotalwhenever not searching:return isSearching ? searchTotal : storeTotal;web/src/App.tsx(L144-157) - header rendersgetDisplayedMemoryCount(explorer.isSearching, explorer.totalItems, explorer.statsTotal).web/src/hooks/useMemoriesExplorer.ts-loadStats()(L55-60) fetches/api/statswith no tag parameter;onTagFilterChange()(L193-205) reloads the list (loadMemories) but never reloads stats.src/services/api-handlers.ts-handleStats()(L719-756) returns global totals across all project + user shards with no tag awareness:total = userCount + projectCount.handleListMemories()(L150-269) does return a correct filteredtotal(timeline.length) - but the frontend only usestotalItemswhenisSearching === true, so the tag-filtered count is discarded.Secondary inconsistency:
handleListTags()(L111-148) only returns project-scoped tags (filters on_project_), whilehandleStatscounts both user and project memories. The displayed "total" can therefore be larger than anything reachable through the tag filter (user-scope memories never appear in the tag dropdown).Suggested fix direction
Either:
/api/stats(or return per-tag counts fromhandleListTags), and display the filtered count when a tag is active; orgetDisplayedMemoryCountto returntotalItemswhen a tag filter is active, not only when searching:return isSearching || tagFilterActive ? searchTotal : storeTotal;Environment