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 contextfrom_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 directorysummary,open_questions,next_steps,files_touched– Human-readable context visible to the next agentowner_user– Optional owner assignment;Nonemakes the handoff public to the entire projectcreated_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.rsfor definitions andcrates/ai-memory-store/src/writer.rsfor persistence logic - Handoffs transition through Open, Accepted, and Expired states managed by the
HandoffStateenum - The
owner_userfield 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →