# How Session Summaries Are Generated in ai-memory: A Deep Dive into the Summary.json Lifecycle

> Discover how ai-memory generates session summaries. Learn about the atomic JSON writes to summary.json and their lifecycle in the transcript module.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-28

---

**Session summaries in ai-memory are generated by the client runtime (Grok, Kiro, Antigravity, etc.) as atomic JSON writes to [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json), then discovered and parsed by the transcript module at [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/transcript.rs).**

The **ai-memory** project uses a standardized [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) format to persist metadata about AI chat sessions. When a session terminates, the producing runtime serializes session identifiers, timing data, token usage, and message counts into this file. The core transcript module later reads this file to enable session discovery, indexing, and resumption.

## What Contains a Session Summary

A session summary is a JSON file located at the root of a Grok-style session directory. The [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) format contains a required `info` object with these fields:

- **`info.id`** — A UUID uniquely identifying the session
- **`info.cwd`** — The working directory the session was attached to
- **`info.start`** / **`info.end`** — ISO 8601 timestamps delimiting the session
- **`info.tokens`** — Token usage statistics (tokens in, tokens out)
- **`info.messageCount`** — Total chat messages in the session
- **`info.additional`** — Optional implementation-specific metadata (LLM model, provider, etc.)

Each session producer (Grok, Kiro v2/v3, Antigravity) assembles these fields based on its own runtime observations.

## How the Transcript Module Discovers Sessions

The **transcript module** at [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/transcript.rs) handles session discovery by parsing [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json). The implementation reads the file and extracts the session identifier and checkout path.

From lines **2606–2620** of [`transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/transcript.rs), the discovery logic:

```rust
let Ok(raw) = fs::read_to_string(session_dir.join("summary.json")) else { … };
let Ok(summary) = serde_json::from_str::<Value>(&raw) else { … };

```

The required fields are extracted from the `info` object:

```rust
let info = summary.get("info").unwrap_or(&Value::Null);
let (Some(id), Some(cwd)) = (
    info.get("id").and_then(Value::as_str),
    info.get("cwd").and_then(Value::as_str),
) else { … };

```

This parsing validates that [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) contains the minimum metadata needed to identify and resume a session.

## Atomic Write Pattern for Summary Generation

Session producers follow a **repository-wide atomic-write convention** to prevent data corruption. The pattern—implemented in [`ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/ai-memory-wiki/src/wiki.rs) and reused across crates—ensures [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) appears atomically at session termination.

The write sequence:

1. Serialize the session metadata to a JSON object
2. Write to a temporary file (`summary.json.tmp`)
3. Call `fsync` to flush to disk
4. `rename` the temporary file to [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json)

This guarantees that readers never observe partially written summaries.

## Complete Code Example: Reading a Session Summary

The transcript module uses this pattern to load session metadata:

```rust
use std::fs;
use std::path::{Path, PathBuf};
use serde_json::Value;

/// Returns the (session-id, cwd) pair from a summary.json file.
fn read_summary(session_dir: &Path) -> std::io::Result<(String, PathBuf)> {
    let raw = fs::read_to_string(session_dir.join("summary.json"))?;
    let json: Value = serde_json::from_str(&raw)?;
    let info = json.get("info").unwrap_or(&Value::Null);
    
    let id = info.get("id").and_then(Value::as_str).ok_or_else(|| {
        std::io::Error::new(std::io::ErrorKind::InvalidData, "missing id")
    })?;
    let cwd = info.get("cwd").and_then(Value::as_str).ok_or_else(|| {
        std::io::Error::new(std::io::ErrorKind::InvalidData, "missing cwd")
    })?;
    
    Ok((id.to_string(), PathBuf::from(cwd)))
}

```

This function returns the session UUID and working directory required to reconstruct the session context.

## Complete Code Example: Generating a Session Summary

A session producer implements generation like this simplified illustration:

```rust
use std::fs;
use std::io::Write;
use std::path::Path;
use serde_json::json;
use uuid::Uuid;

/// Called when a session ends; writes `summary.json`.
fn write_summary(session_dir: &Path, id: &Uuid, cwd: &Path) -> std::io::Result<()> {
    let summary = json!({
        "info": {
            "id": id.to_string(),
            "cwd": cwd.to_string_lossy(),
            "start": chrono::Utc::now().to_rfc3339(),
            "end": chrono::Utc::now().to_rfc3339(),
            "tokens": 12345,
            "messageCount": 27
        }
    });
    
    // Atomic write: write to a temp file then rename
    let tmp_path = session_dir.join("summary.json.tmp");
    let mut tmp = fs::File::create(&tmp_path)?;
    tmp.write_all(summary.to_string().as_bytes())?;
    tmp.sync_all()?; // fsync
    fs::rename(tmp_path, session_dir.join("summary.json"))?;
    
    Ok(())
}

```

The `sync_all()` call ensures durability before the atomic `rename` makes the file visible to readers.

## Key Source Files

| File | Role |
|------|------|
| [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/transcript.rs) | Discovers sessions by reading [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) (lines 2606–2620) |
| [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs) | Contains the generic atomic-write helper for persisting summaries |
| Session-producer crates (`ai-memory-workstream`, `ai-memory-wiki`, etc.) | Implement concrete logic for assembling and writing JSON at session termination |

## Summary

- **Session summaries** are JSON files named [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) living in session directories
- **Generation** occurs at session termination by the runtime that produced the session (Grok, Kiro, Antigravity)
- **Atomic writes** via temp-file-and-rename prevent corruption and ensure readers see complete data
- **Discovery** happens in [`transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/transcript.rs) at lines 2606–2620, where `serde_json` parses the file and extracts `info.id` and `info.cwd`
- The format standardizes session identity, timing, token usage, and message counts across all ai-memory producers

## Frequently Asked Questions

### What happens if [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) is missing or corrupted?

The transcript module skips the directory. The `read_to_string` and `serde_json::from_str` calls both return `Err` variants that the caller handles by logging and continuing to the next session directory. No partial session data is exposed to downstream consumers.

### Can session producers extend the [`summary.json`](https://github.com/akitaonrails/ai-memory/blob/main/summary.json) format?

Yes, through the `info.additional` field. The base format requires only `id` and `cwd`; producers may attach implementation-specific metadata such as LLM model names, API providers, or custom telemetry without breaking the core discovery logic.

### Why does ai-memory use atomic writes for JSON files?

The atomic temp-file-and-rename pattern prevents readers from observing partially written JSON during crashes or power failures. The `fsync` before `rename` guarantees that the data reaches stable storage, and `rename` is atomic on POSIX systems. This same pattern appears throughout ai-memory's persistence layer, including in [`ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/ai-memory-wiki/src/wiki.rs).