Understanding the Handoff Row in ai-memory Session Synthesis

A Handoff row is a first-class snapshot of a finished agent session stored in SQLite that captures the working directory, summary, open questions, and touched files, enabling the next agent to resume work without re-parsing the full observation log.

The ai-memory project implements a sophisticated session synthesis layer that allows AI agents to maintain continuity across separate CLI invocations. When an agent terminates a session using memory_handoff_end, the system creates a Handoff row in the SQLite store that the next agent can claim via memory_handoff_accept. This mechanism eliminates the need to rebuild context from scratch or re-parse entire observation logs.

What Constitutes a Handoff Row

According to the source code in crates/ai-memory-core/src/handoff.rs, a Handoff row encapsulates all ephemeral state required to resume work across agents. Unlike wiki entries that survive re-indexing, handoffs represent "DB-only episodic state" as noted in crates/ai-memory-store/src/reader.rs (lines 716-720).

Core Schema Fields

The Handoff structure stores the following critical fields:

  • workspace_id / project_id – Scoping identifiers ensuring the handoff belongs to a specific workspace and project context
  • from_session_id – Tracks the session that produced the handoff (optional for manual handoffs)
  • from_agent / to_agent – The producing agent and an optional target-agent hint (e.g., claude-code, codex)
  • cwd – Working directory at handoff time, used to match the next session's current working directory
  • summary, open_questions, next_steps, files_touched – Human-readable context visible to the next agent
  • owner_user – Optional owner assignment; None makes the handoff public to the entire project
  • created_at / accepted_at – Timestamps for lifecycle tracking

The HandoffState Machine

The HandoffState enum defines three distinct lifecycle states implemented in the core library:

  • Open – Freshly created and awaiting acceptance
  • Accepted – Claimed by another agent via memory_handoff_accept
  • Expired – Removed by the decay sweep process

Creating and Claiming Handoffs

The ai-memory store provides explicit methods for handoff management through the writer interface in crates/ai-memory-store/src/writer.rs.

Inserting a New Handoff

When ending a session, agents construct a NewHandoff struct and persist it using insert_handoff:

let new_handoff = ai_memory_core::NewHandoff {
    workspace_id,
    project_id,
    from_session_id: Some(session_id),
    from_agent: ai_memory_core::AgentKind::Codex,
    to_agent: None,
    cwd: Some(std::env::current_dir()?.into()),
    summary: "Finished data-cleaning step".into(),
    open_questions: vec!["How to handle missing values?".into()],
    next_steps: vec!["Run `memory_handoff_accept`".into()],
    files_touched: vec!["src/cleaner.rs".into()],
    owner_user: Some("alice".into()),
};
let handoff_id = store.writer.insert_handoff(new_handoff).await?;

The low-level SQL implementation resides in crates/ai-memory-store/src/ops.rs within the insert_handoff_row function.

Accepting a Handoff

The next agent claims ownership using the accept_handoff method with a HandoffAcceptance structure:

let acceptance = ai_memory_core::HandoffAcceptance {
    handoff_id,
    workspace_id,
    project_id,
    accepting_agent: ai_memory_core::AgentKind::Codex,
    accepting_session: Some(new_session_id),
    accepting_user: Some("bob".into()),
    owner_filter: ai_memory_core::OwnerFilter::Any,
    receiving_cwd: Some(std::env::current_dir()?.to_string_lossy().into()),
};
let accepted = store.writer.accept_handoff(acceptance).await?;
assert!(accepted);

Querying Pending Handoffs

Agents can list available handoffs using the reader interface, typically invoked by memory_briefing:

let pending = store.reader.list_handoffs(
    workspace_id,
    project_id,
    Some(ai_memory_core::HandoffState::Open),
    ai_memory_core::OwnerFilter::Any,
    50,
).await?;
println!("Open handoffs: {}", pending.len());

Database Architecture and Persistence

Handoffs occupy a unique position in the ai-memory storage hierarchy. While the system rebuilds wiki content from observations during re-indexing, handoff rows remain untouched as pure database entities.

Storage Implementation

The crates/ai-memory-store/src/reader.rs implementation explicitly distinguishes handoffs as ephemeral state that persists solely in SQLite. This design choice ensures that session synthesis data survives wiki rebuilds without requiring re-derivation from observation logs.

Ownership and Access Control

The owner_user field implements a flexible permission model. When set to None, any project member can claim the handoff; when populated with a specific username, only that teammate can accept it. The test suite in crates/ai-memory-store/tests/handoff_ownership.rs validates these ownership constraints through comprehensive integration tests.

Summary

  • A Handoff row captures a complete snapshot of session state including working directory, touched files, and unresolved questions
  • The implementation spans crates/ai-memory-core/src/handoff.rs for definitions and crates/ai-memory-store/src/writer.rs for persistence logic
  • Handoffs transition through Open, Accepted, and Expired states managed by the HandoffState enum
  • The owner_user field enables both public handoffs and private assignments to specific teammates
  • Unlike wiki entries, handoffs exist as "DB-only episodic state" that survives re-indexing operations

Frequently Asked Questions

How does a Handoff row differ from regular session memory in ai-memory?

Regular session memory typically consists of observation logs and wiki entries that the system can rebuild from source data. A Handoff row constitutes a first-class, immutable snapshot stored directly in SQLite that preserves the exact state of a finished session without requiring re-parsing or re-indexing. This distinction allows agents to resume work immediately by reading a single row rather than reconstructing context from scattered observations.

What triggers the Expired state in the Handoff state machine?

The Expired state results from a decay sweep process that removes stale handoffs from the database. Unlike the Open and Accepted states which transition based on explicit agent actions (creation and acceptance respectively), expiration occurs automatically when the system identifies handoffs that have remained unclaimed beyond their configured retention period.

Can multiple agents claim the same Handoff simultaneously?

No. The acceptance mechanism in crates/ai-memory-store/src/writer.rs implements atomic claim operations. Once an agent successfully invokes accept_handoff and transitions the row from Open to Accepted, subsequent attempts to claim that specific handoff_id will fail. The owner_filter parameter in HandoffAcceptance allows agents to specify ownership constraints, but the underlying implementation ensures single-agent ownership per handoff.

Where is the SQL implementation for inserting Handoff rows located?

The low-level SQL operations reside in crates/ai-memory-store/src/ops.rs, specifically within the insert_handoff_row function. This module provides the raw database interface that writer.rs utilizes, while the high-level API for creating handoffs is exposed through crates/ai-memory-store/src/writer.rs with the insert_handoff method.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →