Migrate seedInitialMemories to v2 memory plugin before A3 drops agent_memories #1755

Closed
opened 2026-05-24 00:07:53 +00:00 by hongming · 0 comments
Owner

Migrate seedInitialMemories to the v2 memory plugin before A3 drops agent_memories

Problem

After PR #1747 (Phase A1) lands, the MCP memory tools (commit_memory, recall_memory) read and write exclusively through the v2 plugin. But seedInitialMemories in workspace-server/internal/handlers/workspace_provision.go still writes directly into the legacy agent_memories table at workspace-create time.

Result: a workspace provisioned with initial_memories (used by POST /workspaces body and org/import templates) has rows in agent_memories that are invisible to recall_memory — every fresh workspace that depends on seeded memories silently has none.

This was flagged in the #1747 hostile review (finding C3) and deliberately deferred to keep the A1 PR small.

Affected call sites

$ git grep -n "seedInitialMemories(" workspace-server/internal/handlers/
internal/handlers/workspace_provision.go:197:func seedInitialMemories(ctx context.Context, workspaceID string, memories []models.MemorySeed) {
internal/handlers/workspace.go:575:    seedInitialMemories(ctx, id, payload.InitialMemories)
internal/handlers/org.go:808:           seedInitialMemories(context.Background(), rootID, globalSeeds)
internal/handlers/org_import.go:262:    seedInitialMemories(ctx, id, wsMemories)

Three writer call paths (workspace create, org-template root global memories, org-import per-workspace), one shared helper that does INSERT INTO agent_memories.

Proposed fix

Migrate seedInitialMemories to call h.memv2.plugin.CommitMemory(...) via the same shim path agents use:

// rough sketch — actual implementation needs the v2 dep wired into
// WorkspaceHandler the same way MCPHandler.memv2 is wired
func seedInitialMemories(ctx context.Context, plugin *client.Client, resolver *namespace.Resolver, workspaceID string, memories []models.MemorySeed) {
    if len(memories) == 0 {
        return
    }
    ns := workspaceMemoryNamespace(workspaceID)  // "workspace:<id>"
    for _, mem := range memories {
        scope := strings.ToUpper(mem.Scope)
        if scope == "" {
            scope = "LOCAL"
        }
        // existing scope/content validation + redactSecrets stays
        if _, err := plugin.CommitMemory(ctx, ns, contract.MemoryWrite{
            Content: content,
            Kind:    contract.MemoryKindFact,
            Source:  contract.MemorySourceRuntime, // seed = runtime-injected, not agent
        }); err != nil {
            log.Printf("seedInitialMemories: plugin commit failed for %s: %v", workspaceID, err)
        }
    }
}

Plus wire the v2 deps into WorkspaceHandler (parallel to how MCPHandler.WithMemoryV2 works).

Why this is gated on A1, not blocking it

  • Today (pre-A1): every memory write goes to agent_memories via the legacy SQL fallback. seedInitialMemories writing there is consistent with the rest of the system.
  • Post-A1, pre-A3: agent writes go to v2 plugin, seeds still go to agent_memories. Inconsistent (this issue's window).
  • Post-A3: agent_memories is dropped. seedInitialMemories would error on every workspace create until migrated. Migrating BEFORE A3 ships is mandatory.

So: file this now (#1747 review action), ship A1, ship this PR, then A3 can land cleanly.

Test rework cost

Every seedInitialMemories test in workspace_provision_test.go (5+ tests) currently asserts on ExpectExec("INSERT INTO agent_memories"). After migration they'd assert on a stub plugin's CommitMemory captures, mirroring the pattern in mcp_tools_memory_legacy_shim_test.go. About a 1-hour refactor.

Risk

  • Source attribution: agent_memories rows from seeds carry no source distinction. Plugin's MemoryWrite.Source enum has agent | runtime | user. Suggest runtime for seeds (matches the semantic "platform-provided baseline knowledge"). Verify with CTO before landing.
  • Workspace-create transactional shape: seedInitialMemories runs in a goroutine (log.Printf on error, non-fatal). The v2 plugin call could fail / time out — preserve the non-fatal semantics so workspace creation doesn't depend on plugin health.
  • No backfill needed: initial_memories is consumed at workspace-create time only; existing workspaces don't re-seed on restart. So no historical migration of pre-existing seed rows is required.

Refs

  • #1747 (Phase A1 — the PR that exposes this gap)
  • #1733 (parent issue — memory SSOT consolidation, mentions Phase A3 as the table drop)
  • Review finding C3 of #1747

Tier

area:memory tier:medium — must land before Phase A3 (drop agent_memories table) or workspace creation breaks. No security concern; no API surface change.

# Migrate `seedInitialMemories` to the v2 memory plugin before A3 drops `agent_memories` ## Problem After PR #1747 (Phase A1) lands, the MCP memory tools (`commit_memory`, `recall_memory`) read and write exclusively through the v2 plugin. But `seedInitialMemories` in `workspace-server/internal/handlers/workspace_provision.go` **still writes directly into the legacy `agent_memories` table** at workspace-create time. Result: a workspace provisioned with `initial_memories` (used by `POST /workspaces` body and org/import templates) has rows in `agent_memories` that are **invisible to `recall_memory`** — every fresh workspace that depends on seeded memories silently has none. This was flagged in the #1747 hostile review (finding C3) and deliberately deferred to keep the A1 PR small. ## Affected call sites ``` $ git grep -n "seedInitialMemories(" workspace-server/internal/handlers/ internal/handlers/workspace_provision.go:197:func seedInitialMemories(ctx context.Context, workspaceID string, memories []models.MemorySeed) { internal/handlers/workspace.go:575: seedInitialMemories(ctx, id, payload.InitialMemories) internal/handlers/org.go:808: seedInitialMemories(context.Background(), rootID, globalSeeds) internal/handlers/org_import.go:262: seedInitialMemories(ctx, id, wsMemories) ``` Three writer call paths (workspace create, org-template root global memories, org-import per-workspace), one shared helper that does `INSERT INTO agent_memories`. ## Proposed fix Migrate `seedInitialMemories` to call `h.memv2.plugin.CommitMemory(...)` via the same shim path agents use: ```go // rough sketch — actual implementation needs the v2 dep wired into // WorkspaceHandler the same way MCPHandler.memv2 is wired func seedInitialMemories(ctx context.Context, plugin *client.Client, resolver *namespace.Resolver, workspaceID string, memories []models.MemorySeed) { if len(memories) == 0 { return } ns := workspaceMemoryNamespace(workspaceID) // "workspace:<id>" for _, mem := range memories { scope := strings.ToUpper(mem.Scope) if scope == "" { scope = "LOCAL" } // existing scope/content validation + redactSecrets stays if _, err := plugin.CommitMemory(ctx, ns, contract.MemoryWrite{ Content: content, Kind: contract.MemoryKindFact, Source: contract.MemorySourceRuntime, // seed = runtime-injected, not agent }); err != nil { log.Printf("seedInitialMemories: plugin commit failed for %s: %v", workspaceID, err) } } } ``` Plus wire the v2 deps into `WorkspaceHandler` (parallel to how `MCPHandler.WithMemoryV2` works). ## Why this is gated on A1, not blocking it - **Today (pre-A1)**: every memory write goes to `agent_memories` via the legacy SQL fallback. `seedInitialMemories` writing there is consistent with the rest of the system. - **Post-A1, pre-A3**: agent writes go to v2 plugin, seeds still go to `agent_memories`. Inconsistent (this issue's window). - **Post-A3**: `agent_memories` is dropped. `seedInitialMemories` would error on every workspace create until migrated. Migrating BEFORE A3 ships is mandatory. So: file this now (#1747 review action), ship A1, ship this PR, then A3 can land cleanly. ## Test rework cost Every `seedInitialMemories` test in `workspace_provision_test.go` (5+ tests) currently asserts on `ExpectExec("INSERT INTO agent_memories")`. After migration they'd assert on a stub plugin's CommitMemory captures, mirroring the pattern in `mcp_tools_memory_legacy_shim_test.go`. About a 1-hour refactor. ## Risk - **Source attribution**: `agent_memories` rows from seeds carry no source distinction. Plugin's `MemoryWrite.Source` enum has `agent | runtime | user`. Suggest `runtime` for seeds (matches the semantic "platform-provided baseline knowledge"). Verify with CTO before landing. - **Workspace-create transactional shape**: `seedInitialMemories` runs in a goroutine (`log.Printf` on error, non-fatal). The v2 plugin call could fail / time out — preserve the non-fatal semantics so workspace creation doesn't depend on plugin health. - **No backfill needed**: `initial_memories` is consumed at workspace-create time only; existing workspaces don't re-seed on restart. So no historical migration of pre-existing seed rows is required. ## Refs - #1747 (Phase A1 — the PR that exposes this gap) - #1733 (parent issue — memory SSOT consolidation, mentions Phase A3 as the table drop) - Review finding C3 of #1747 ## Tier `area:memory` `tier:medium` — must land before Phase A3 (drop `agent_memories` table) or workspace creation breaks. No security concern; no API surface change.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1755