Skip to content

MemorySourceType declares a "user" variant that nothing ever writes — operator-stated facts are indistinguishable from agent inference #334

Description

@iceteaSA

Summary

MemorySourceType has four variants and the dashboard renders all four, but only three are reachable. Nothing in the codebase ever writes sourceType: "user", so a fact the operator stated is stored byte-identically to one an agent inferred on its own.

Small and self-contained. Filing separately because it stands on its own regardless of the larger proposal that motivated me to notice it (see #335).

Evidence

All references against upstream/master @ b27a6d76 (v0.38.0).

The type declares it — packages/plugin/src/features/magic-context/memory/types.ts:25:

export type MemorySourceType = "historian" | "agent" | "dreamer" | "user";

The dashboard already renders it — packages/dashboard/src/components/MemoryBrowser/MemoryBrowser.tsx:342-350:

case "historian": return "purple";
case "agent":     return "blue";
case "dreamer":   return "indigo";
case "user":      return "green";     // ← unreachable

But every write site is one of the other three:

memory/promotion.ts:75                    sourceType: "historian"
memory/memory-migration.ts:233            sourceType: "historian"
dreamer/retrospective-learnings.ts:190    sourceType: "dreamer"
tools/ctx-memory/tools.ts:291             return deps.sourceType ?? "agent"

A repo-wide search for sourceType: "user" across non-test .ts on master returns nothing. On my store (1,431 active memories) the live distribution is agent 677 / historian 467 / dreamer 287 / user 0.

Why it matters

When the operator says "always use X" and the agent records it with ctx_memory, the row lands as source_type='agent' — the same value as a memory the agent minted from its own reasoning with no human input. Downstream that means:

  • The dashboard's green user badge can never appear, so the operator cannot visually separate their own stated rules from agent inferences when auditing the store.
  • curate / verify cannot weight operator-stated facts differently. An agent inference and an operator instruction are equally eligible for archival. Given that archiving an operator-stated rule is strictly worse than archiving an agent guess, there is currently no signal to protect the former.
  • Any future work wanting "operator said so" as a trust input has no column to read.

Scope

The type, the column (source_type TEXT DEFAULT 'historian', storage-db.ts:1047), and the dashboard arm all already exist — this is a write-path gap, not a schema change. No migration needed.

The design question is how ctx_memory decides a write is operator-attributed. getSourceType (tools.ts:290-292) already reads an injectable deps.sourceType, so the plumbing is there. Options, roughly in increasing cost:

  1. An explicit optional tool parameter (attribution: "operator") the agent sets when recording something the operator stated. Cheapest; relies on the agent being honest, which is the same trust level as every other field it writes.
  2. Host-side inference at the write boundary — e.g. attribute to user when the current turn's triggering message is a real (non-synthetic) user message. More robust, more machinery.
  3. Leave writes alone and treat the variant as dashboard-only vestigial — in which case the honest fix is deleting it from the type so it stops implying a distinction that does not exist.

I lean (1), but this is your call and I have not written any of it. Happy to send a PR for whichever shape you prefer, or to close this if (3) is the intent.

Correction on my own claim

I initially told a peer this variant did not exist at all, generalizing from its absence in live data to its absence in the schema. That was wrong — the enum arm and the dashboard rendering were there the whole time, which makes this a materially smaller ask than I first described. Verified from source before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions