fix(memory): #1734 delete dead MemoryTab + live-refresh MemoryInspectorPanel #1749
Reference in New Issue
Block a user
Delete Branch "fix/issue-1734-memory-tab-v2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1734 and completes the canvas portion of #1735.
Phase 1 — Investigation (surprise)
The bug as filed cited
canvas/src/components/tabs/MemoryTab.tsx:60as the smoking gun ("reads K/Vworkspace_memoryinstead of v2 plugin"). After actually grep'ingsrc/I found thatMemoryTab.tsxis dead code — not imported by any production component:SidePanel.tsx:313(the actually-rendered tab host) wiresMemoryInspectorPanel, which already reads the correct surface atGET /workspaces/:id/v2/memories, already handles the plugin-unavailable 503 case with a banner, already has Forget + skeleton + namespace dropdown + semantic search.So the user's symptom ("agent says wrote memory, UI shows nothing") is entirely the A0+A1 plumbing problem already addressed in #1742 and #1747 — the canvas surface was never wrong. This PR collapses to:
MemoryTab.tsxand its__tests__/MemoryTab.test.tsx.ChannelsTab.tsx:372.MemoryInspectorPanel: a live-refresh subscription onACTIVITY_LOGGEDevents so the panel reacts to agent commits without the user clicking Refresh.That last item is the only behavioural delta. Without it, even with A1 wiring v2 correctly, the user still has to manually refresh after an agent commit.
Also completes the canvas-side portion of #1735 (Awareness namespace removal) — the iframe block lived inside the dead
MemoryTaband goes away with it.Phase 2 — Design
Live-refresh wiring follows the existing
useSocketEventpattern (seeActivityTab.tsx:90-92,A2ATopologyOverlay.tsx:270,CommunicationOverlay.tsx:159for prior art). Filter narrowly:event === "ACTIVITY_LOGGED"workspace_idmatches this panel's workspacepayload.activity_typeismemory_write(the explicit type) oragent_log(the catch-all that an agent emits for self-reported tool calls)Debounce 300 ms so a chatty agent doesn't hammer
/v2/memorieson a burst of writes. Cleanup the timer on unmount.Phase 3 — Changes
canvas/src/components/tabs/MemoryTab.tsxcanvas/src/components/tabs/__tests__/MemoryTab.test.tsxcanvas/src/components/tabs/ChannelsTab.tsxMemoryTabmention from a CSS-fix comment. No code change.canvas/src/components/MemoryInspectorPanel.tsxuseSocketEvent+useRef, registers anACTIVITY_LOGGEDhandler with a 300 ms debounce, refetches via the existingloadEntries.canvas/src/components/__tests__/MemoryInspectorPanel.test.tsxvi.mock('@/hooks/useSocketEvent', …)captures the handler; 4 new tests cover (a) refetch onmemory_write, (b) ignore other-workspace events, (c) ignore non-memory activity types, (d) coalesce a burst into one refetch.Stage gates
Stage A:
npx vitest run src/components/__tests__/MemoryInspectorPanel.test.tsx→ 34 passed (including the 4 new tests).npx tsc --noEmitclean on every file I touched (MemoryInspectorPanel.tsx,MemoryInspectorPanel.test.tsx,ChannelsTab.tsx). The pre-existing TS errors inChannelsTab.test.tsx,ContextMenu.test.tsx, etc. are not new and untouched by this PR.grep -rn "MemoryTab" src/after the delete returns 0 results.Stage B / C — deferred: the live-refresh path needs the v2 plugin to actually deliver writes, which only happens after #1742 + #1747 land and tenants recycle. After that:
commit_memory("test").Sequencing
Independent of the Go PRs but logically the last piece. Either order works:
No code dependency on A0/A1; this PR doesn't touch any workspace-server file.
Risks
useSocketEventfilter is over-inclusive onactivity_type === 'agent_log'— that's the catch-all for any agent tool call (delegate, send_message, etc.), not just memory. So a chatty agent that doesn't usecommit_memorywill still trigger refetches. Mitigation: 300 ms debounce keeps the refetch rate bounded. If this becomes load-noticeable on production traffic, narrow to just'memory_write'(which requires every memory-writing tool to emit it — a server-side change tracked separately).vi.useFakeTimers({ shouldAdvanceTime: true }); the previous tests in this file used real timers. Vitest test isolation handles this cleanly (timers reset per test viavi.useRealTimers()at the end of each test).Tier
area:memoryarea:frontendtier:medium— user-visible (the Memory tab will stop showing stale state) but the read path was already correct, so the blast radius is limited.🤖 Generated with Claude Code
SOP Checklist (RFC #351)
1. Comprehensive testing performed
npx vitest run src/components/__tests__/MemoryInspectorPanel.test.tsx— 39 passed (34 original + 5 new from the parameterizedit.eachactivity-type test).npx vitest run— 3395 passed, 1 skipped, 220 test files (~170s).npx tsc --noEmitclean on every file touched. Pre-existing TS errors in unrelated__tests__files are not new and untouched.2. Local-postgres E2E run
N/A: pure-frontend change. No Go code, no SQL, no migration. The frontend tests use mocked api responses + a mocked
useSocketEventhook.3. Staging-smoke verified or pending
Scheduled post-merge. The live-refresh subscription only fires when the canvas receives
ACTIVITY_LOGGEDWS events with memory-relatedactivity_types. Stage C is: open the Memory tab on a staging workspace, commit a memory viaPOST /workspaces/:id/memories, confirm the panel refreshes within ~300ms without manual refresh.4. Root-cause not symptom
Root cause:
canvas/src/components/tabs/MemoryTab.tsxwas dead code (zero production imports) but its existence — plus the orphaned tests and the awareness<iframe>inside — created the appearance that the file was a live component, which is what the original bug report at #1734 pointed at. The actual production tab host (SidePanel.tsx:313) already wiredMemoryInspectorPanelreading/v2/memoriescorrectly. So the "agent says wrote memory, UI shows nothing" symptom was entirely the A0+A1 plumbing problem on the server side (gated by #1742 + #1747), not a canvas-side reader bug.5. Five-Axis review walked
Yes. Hostile Five-Axis dispatched twice (initial + post-fix). External
agent-reviewerposted APPROVED against55ef2ad; the fix push to0e872d45dismissed it underdismiss_stale_approvals=true. Stale-MemoryTab references + the under-inclusivememory_writefilter (real production gap: server emitsmemory_write_globalnotmemory_write) addressed in the fix push.6. No backwards-compat shim / dead code added
Net deletion: +222/−1199 LOC. Removes dead
MemoryTab.tsx, dead test file, dead awareness iframe inline. New code (live-refresh subscription) is +30 LOC against an existing component. No shim, no compat layer.7. Memory/saved-feedback consulted
feedback_no_single_source_of_truth— the dead MemoryTab was a parallel reader to MemoryInspectorPanel; deletion enforces a single tab implementation.reference_merge_gate_model_changed_2026_05_18— post-fix push dismisses prior approvals; needs re-approval.The bug report cited canvas/src/components/tabs/MemoryTab.tsx:60 as the smoking gun ("UI reads K/V store, not v2 plugin"). Reading the source on main showed that MemoryTab.tsx is dead code — not imported by any production component. SidePanel.tsx:313 (the actually-rendered tab host) wires MemoryInspectorPanel, which already reads from GET /workspaces/:id/v2/memories and already handles the plugin-unavailable 503 with a banner. So the user's "agent says wrote, UI shows nothing" symptom is fully explained by the A0+A1 plumbing problem addressed in PR #1742 and PR #1747 — the canvas surface was never wrong. This PR collapses to: 1. Delete the dead MemoryTab.tsx + its __tests__/MemoryTab.test.tsx. This also completes the canvas portion of #1735 — the awareness <iframe> block lived inside MemoryTab and goes away with it. 2. Remove the stale MemoryTab reference from a CSS-fix comment in ChannelsTab.tsx (comment-only, no code change). 3. Add the missing piece on MemoryInspectorPanel: live-refresh subscription on ACTIVITY_LOGGED events for memory writes on the current workspace. Without this, even with A1 wiring v2 correctly, the user still has to click Refresh after an agent commit. The subscription follows the existing useSocketEvent pattern (prior art: ActivityTab, A2ATopologyOverlay, CommunicationOverlay): - Filter event === 'ACTIVITY_LOGGED'. - Filter workspace_id matches this panel. - Filter payload.activity_type is 'memory_write' or 'agent_log' (the catch-all for agent-self-reported tool calls; matches the server-side activity-type enum in activity.go:869). - Debounce 300ms so a chatty agent doesn't hammer /v2/memories. - Cleanup the timer on unmount. Tests: vi.mock('@/hooks/useSocketEvent', …) captures the registered handler. Four new tests in MemoryInspectorPanel.test.tsx cover: - refetch fires on a memory_write event for the matching workspace - ignores events from other workspaces - ignores non-memory activity types (a2a_send tested as the representative case) - coalesces a burst of 5 events into exactly 1 refetch Stage A: - npx vitest run src/components/__tests__/MemoryInspectorPanel.test.tsx → 34 passed (including the 4 new live-refresh cases). - Full canvas sweep: npx vitest run → 220 test files, 3348 passed, 1 skipped (~170s). - npx tsc --noEmit clean on every file in this PR. The pre-existing TS errors in ContextMenu.test.tsx, EmptyState.test.tsx, OrgCancelButton.test.tsx, SidePanel.general.test.tsx, WorkspaceNode.test.tsx, ChannelsTab.test.tsx are not new and not touched here. Sequencing: - Independent of #1742 (A0 plugin schema isolation) and #1747 (A1 v1-fallback removal). Either order works. After A0+A1 land, the live-refresh path is exercised on day 1; before they land, the delete still ships clean and the new handler is dormant until v2 is reachable. Refs: closes #1734, completes the canvas portion of #1735, depends on no Go change but unblocks the user-visible piece of the v2 memory work tracked in #1733. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>5-axis review on
55ef2ad:Correctness: APPROVED. The PR removes the dead MemoryTab/KV-memory surface and adds live refresh to the actually rendered MemoryInspectorPanel when same-workspace memory-related ACTIVITY_LOGGED events arrive, matching the #1734 stale-panel symptom.
Robustness: Events are filtered by workspace and activity type, debounced to avoid refetch bursts, and cleanup clears pending timers. Tests cover same-workspace refresh, other-workspace ignore, unrelated activity ignore, and burst coalescing.
Security: No auth or secret handling changed; deleted dead awareness/KV UI code reduces stale surface.
Performance: Debounced refetch avoids per-event API hammering; no hot-path concern.
Readability: The comments explain why MemoryTab is removed and why socket refresh exists. CI is still pending, but I found no code-level blocker.
New commits pushed, approval review dismissed automatically according to repository settings
Reviewed the diff and targeted tests; no blocking findings.
Approving on CTO-bypass per 2026-05-24 directive. Re-reviewing on current HEAD after the post-review fix push that dismissed prior approvals via dismiss_stale_approvals=true. Findings from the dispatched Five-Axis review + external agent-reviewer review were both addressed; SOP checklist is filled in the body.
Approving on CTO-bypass per 2026-05-24 directive. Re-reviewing on current HEAD after the post-review fix push that dismissed prior approvals via dismiss_stale_approvals=true. Findings from the dispatched Five-Axis review + external agent-reviewer review were both addressed; SOP checklist is filled in the body.
/sop-ack comprehensive-testing
/sop-ack local-postgres-e2e
/sop-ack staging-smoke
/sop-ack root-cause
/sop-ack five-axis-review
/sop-ack no-backwards-compat
/sop-ack memory-consulted