Jidoka memory is a small set of data contracts and a single Jidoka.Memory.Store behaviour. The agent spec declares a memory policy; the runtime turns that policy into recall and write requests; a pluggable store answers those requests. This guide documents each struct and the store interface so that custom stores (Postgres, Redis, vector DB, etc.) can interoperate without guesswork.

When To Use This

  • Use this guide when you need the exact shape of a memory entry, recall, or write to build a store, replay tool, or audit query.
  • Use this guide when wiring Jidoka.Memory.Store.InMemory into tests.
  • Do not use this guide as a memory tutorial. The high-level workflow lives in Runtime And Harness.

Prerequisites

Quick Example

A complete memory round-trip uses the in-memory store and the three core contracts: WriteRequest, RecallRequest, RecallResult.

alias Jidoka.Memory.{Entry, RecallRequest, WriteRequest, Store}

{:ok, pid} = Jidoka.Memory.Store.InMemory.start_link([])
store = {Jidoka.Memory.Store.InMemory, pid: pid}

entry = Entry.new!(agent_id: "time_agent", content: "User prefers Chicago time")
{:ok, _write} = Store.write(store, WriteRequest.new!(entry: entry))

request =
  RecallRequest.new!(
    agent_id: "time_agent",
    scope: :agent,
    query: "preferred timezone",
    limit: 3
  )

{:ok, recall} = Store.recall(store, request)
length(recall.entries)
#=> 1

Concepts

╭──────────────────╮     ╭───────────────────╮     ╭──────────────────╮
│ Spec.Memory      │────▶│ Runtime           │────▶│ Memory.Store     │
│ (policy)         │     │ assembles recall  │     │ (behaviour)      │
╰──────────────────╯     ╰─────────┬─────────╯     ╰────────┬─────────╯
                                   ▼                        ▼
                          ╭──────────────────╮      ╭──────────────────╮
                          │ RecallRequest    │      │ RecallResult     │
                          │ WriteRequest     │      │ WriteResult      │
                          ╰──────────────────╯      ╰──────────────────╯

Spec.Memory is policy (definition data). Stores are supplied per run through harness options. The runtime negotiates the conversation by emitting RecallRequest and WriteRequest structs and reading RecallResult / WriteResult back.

Fields

Jidoka.Memory.Entry

Durable memory entry available to prompt assembly.

FieldTypeDefaultPurpose
idnon-empty stringgenerated "mem_…"Stable id for upsert/dedupe.
agent_idnon-empty stringrequiredOwning agent (matches Spec.id).
session_idnon-empty string or nilnilSession scope when applicable.
contentnon-empty stringrequiredContent injected into the prompt or context.
metadatamap%{}Arbitrary caller metadata.

Jidoka.Memory.RecallRequest

Request sent to a store before prompt assembly.

FieldTypeDefaultPurpose
agent_idnon-empty stringrequiredWhose memory to read.
session_idnon-empty string or nilnilSession scope for :session policies.
scope:agent | :session:agentMirrors Spec.Memory.scope.
querynon-empty stringrequiredQuery used by the store (free text, embedding key, etc.).
limitpositive integer5Maximum entries to return.
metadatamap%{}Caller metadata for tracing or routing.

Jidoka.Memory.RecallResult

Result returned by Memory.Store.recall/2.

FieldTypeDefaultPurpose
requestRecallRequest.t()requiredThe original recall request (echoed for trace clarity).
entries[Entry.t()][]Recalled entries in store-defined order.
metadatamap%{}Store metadata (similarity scores, latency, etc.).

Jidoka.Memory.WriteRequest

Request to upsert one entry.

FieldTypeDefaultPurpose
entryEntry.t()requiredEntry to persist.
metadatamap%{}Write-specific metadata.

Jidoka.Memory.WriteResult

Acknowledgement returned by Memory.Store.write/2.

FieldTypeDefaultPurpose
requestWriteRequest.t()requiredThe original write request.
entryEntry.t()requiredThe (possibly normalized) entry as stored.
status:ok:okReserved for future statuses; today always :ok.
metadatamap%{}Store metadata.

Jidoka.Memory.Store Behaviour

Three callbacks define the store contract. A store is a module or {module, opts} tuple.

CallbackPurpose
recall(RecallRequest.t(), opts) :: {:ok, RecallResult.t()} | {:error, term()}Read entries matching the request.
write(WriteRequest.t(), opts) :: {:ok, WriteResult.t()} | {:error, term()}Upsert one entry.
list_entries(opts) :: {:ok, [Entry.t()]} | {:error, term()}Diagnostic listing for tests and inspectors.

Top-level helpers Memory.Store.recall/2, Memory.Store.write/2, and Memory.Store.list_entries/1 normalize the {module, opts} shape before dispatching.

Common Patterns

  • Reuse Entry.new!/1 to generate ids. The default "mem_…" id is enough for most stores; supply your own only when integrating with an external primary key.
  • Carry routing data in metadata. Stores should ignore unknown keys, so feel free to thread tenant ids, embedding model names, or trace ids through any of the request/result maps.
  • Use scope to gate session-only memory. RecallRequest.scope: :session with a session_id lets a store filter cross-session data without app code.
  • Keep stores small. Implementing the three callbacks is enough; the runtime handles policy, capture, and injection.

Testing

The in-memory store is the canonical test fixture. It exercises the full contract without touching disk or network.

setup do
  {:ok, pid} = Jidoka.Memory.Store.InMemory.start_link([])
  {:ok, store: {Jidoka.Memory.Store.InMemory, pid: pid}}
end

test "writes and recalls a single entry", %{store: store} do
  entry = Jidoka.Memory.Entry.new!(agent_id: "demo", content: "hello")

  assert {:ok, _} =
           Jidoka.Memory.Store.write(store,
             Jidoka.Memory.WriteRequest.new!(entry: entry)
           )

  request =
    Jidoka.Memory.RecallRequest.new!(
      agent_id: "demo",
      query: "hello",
      limit: 5
    )

  assert {:ok, recall} = Jidoka.Memory.Store.recall(store, request)
  assert [%{content: "hello"}] = recall.entries
end

Troubleshooting

SymptomLikely CauseFix
ArgumentError: invalid memory entry: ...agent_id or content was empty.Both are required non-empty strings.
Recall returns no entries despite writesScope/session_id mismatch.Use scope: :agent for shared memory; provide a matching session_id for session scope.
in-memory memory store requires :pidUsed Memory.Store.InMemory without the pid: opt.Pass {Jidoka.Memory.Store.InMemory, pid: pid} as the store value.
Recall ignores limitA custom store did not honor RecallRequest.limit.Truncate inside the store; the runtime trusts the result.

Reference