ai-memory v0.8.0

Memory-Kind Vocabulary (Form 6, issue #759; Pillar 2, issue #1709)

v0.7.x extends the substrate’s MemoryKind enum from the original three lifecycle variants (Observation / Reflection / Persona) with the seven-variant Batman taxonomy extension, and v0.8.0 (Pillar 2, #1709) adds the three-variant typed-cognition cluster. The full set is now 13 kinds:

variant purpose
observation direct note from the caller — the default.
reflection curator-synthesised summary over lower-depth peers.
persona curator-generated entity profile (QW-2).
concept abstract definition / vocabulary term.
entity named real-world thing (person, org, product, system).
claim factual assertion the caller is committing to.
relation typed pair / triple anchored in the memory substrate.
event temporally-bounded happening.
conversation captured dialogue turn.
decision choice point with rationale (L1-6 reservation).
goal a desired end-state / objective (Pillar 2 typed-cognition, #1709).
plan an ordered strategy to reach a goal (Pillar 2 typed-cognition, #1709).
step a single executable unit within a plan (Pillar 2 typed-cognition, #1709).

The first three are the v0.7.0 lifecycle variants and are unchanged. The next seven (Form 6) give downstream readers a richer filter-by-kind surface aligned with the Batman framework’s exemplar (Tolaria’s frontmatter-as-type schema). The final three (goal / plan / step, v0.8.0 #1709) are the Pillar-2 typed-cognition kinds: a goal names a desired end-state, a plan is the ordered strategy to reach it, and a step is one executable unit within that plan.

Schema impact: none

The memories.memory_kind TEXT column has no CHECK constraint on either the SQLite or Postgres backends, so the new variants land as new string values on the existing column. No migration required; schema version stays at v37 / v18 respectively. Backward compat:

The fallbacks above are read-path only (row_to_memory widening a stored/unknown value). They do NOT apply to the write path.

Write-path validation (#1467)

On every write surface — CLI store, MCP memory_store, and HTTP POST /api/v1/memories — a supplied kind is validated before the row is created:

Prior to #1467 the MCP and HTTP surfaces silently coerced an unknown/invalid kind to observation while the CLI rejected it; all three surfaces now reject consistently, so a typo’d kind never lands a misclassified row. The canonical accepted set is MemoryKind::all() (the error message is generated from it, never a hardcoded list).

Recall filter

The new kinds parameter on memory_recall (MCP), ?kinds=… (HTTP GET), and kinds: … (HTTP POST body) accepts either:

OR-of-kinds within the param; AND with the other filters (namespace, tags, time-window, visibility). Unknown tokens are silently dropped so a newer client emitting a future variant doesn’t break recall on an older binary.

MCP

{
  "tool": "memory_recall",
  "args": {
    "context": "policy on token rotation",
    "kinds": ["claim", "decision"]
  }
}

HTTP

GET /api/v1/recall?q=policy+rotation&kinds=claim,decision
POST /api/v1/recall
{
  "context": "policy on token rotation",
  "kinds": ["claim", "decision"]
}

CLI

ai-memory recall "policy on token rotation" --kind claim,decision

Auto-classify pre-store hook

The substrate ships a namespace-policy-gated pre-store hook (auto_classify_kind) that may rewrite a stored memory’s memory_kind from the default Observation to a more specific Batman-taxonomy variant. Three policy modes, set on the namespace standard’s metadata.governance JSON blob under auto_classify_kind:

{
  "governance": {
    "auto_classify_kind": "off" | "regex_only" | "regex_then_llm"
  }
}

The caller-supplied kind parameter on memory_store always wins — the hook only fills in Observation (the default) when no kind was set. This keeps explicit-typing callers in full control while giving operators an opt-in path to classify legacy / unstructured content automatically.

Operator surface

The substrate exposes the recall-filter and auto-classify wiring under the memory_kind_vocab block of the v3 capabilities response. Operators can read the live state via:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"memory_capabilities","arguments":{"accept":"3"}}}' \
  | ai-memory mcp --profile core | jq .

(The v0.7-alpha drafts referenced ai-memory doctor --capabilities=v3; that flag was not shipped. The MCP memory_capabilities tool is the canonical inspection surface — it works against any running daemon regardless of profile because memory_capabilities is on the ALWAYS_ON_TOOLS allowlist.)

{
  "vocabulary": ["observation", "reflection", "persona", "concept",
                 "entity", "claim", "relation", "event",
                 "conversation", "decision", "goal", "plan", "step"],
  "recall_filter": "implemented",
  "cli_filter": "implemented",
  "auto_classify": "implemented",
  "auto_classify_modes": ["off", "regex_only", "regex_then_llm"]
}

Forward-compat reservations

Decision is the only L1-6 reservation in the v0.7.x set. The L1-6 work (v0.8.0) will likely add columns for rationale / alternatives on top of the variant; binaries that ship the variant now can already type-tag decisions so downstream readers get a stable filter surface from day one.

Why no schema bump

The original L1-1 work (v0.7.0) landed the memory_kind TEXT NOT NULL DEFAULT 'observation' column under migration 0025 / 0018 without a CHECK constraint. That was a deliberate forward-compat choice: new variants land as new column values; no migration is required to widen the accepted set. The decision is documented in the L1-1 commit and validated by Form 6’s no-migration ship.