# Tuicr Review Session Persistence Layer Explained: File Format, Storage API & Implementation

> Explore Tuicr's review session persistence layer. Discover the JSON file format, storage API, and implementation details for efficient session management.

- Repository: [Almog Gavra/tuicr](https://github.com/agavra/tuicr)
- Tags: internals
- Published: 2026-08-02

---

**Tuicr's review session persistence layer stores each session as a discrete JSON file in `~/.local/share/tuicr/reviews/`, using a flat directory structure with atomic writes, directory-wide locking, and a manifest-based index for fast lookups.**

The review session persistence layer is a core component of [agavra/tuicr](https://github.com/agavra/tuicr), responsible for durably saving, loading, and managing review state between TUI sessions and CLI invocations. This article breaks down the implementation in `src/persistence/`, the session file format, and the design decisions that ensure crash safety and concurrent access.

## Where Sessions Are Stored

Tuicr places all session data under the platform-specific data directory:

- **Linux**: `~/.local/share/tuicr/reviews/`
- **macOS**: `~/Library/Application Support/tuicr/reviews/`
- **Windows**: `%APPDATA%\tuicr\reviews\`

Within this directory, the `sessions/` subdirectory holds individual JSON files, while [`index.json`](https://github.com/agavra/tuicr/blob/main/index.json) serves as the manifest and `.tuicr.lock` provides directory-wide serialization for writes.

## The Persistence Package Structure

The persistence layer spans three modules in `src/persistence/`:

| Module | Responsibility |
|--------|----------------|
| [`storage.rs`](https://github.com/agavra/tuicr/blob/main/storage.rs) | Core API for saving, loading, deleting, and marking sessions as active. Handles atomic writes, directory-wide locking, and migration from older layouts. |
| [`manifest.rs`](https://github.com/agavra/tuicr/blob/main/manifest.rs) | Maintains [`index.json`](https://github.com/agavra/tuicr/blob/main/index.json) that maps slugs → file entries, enabling fast lookups and disambiguation of sessions sharing a slug but differing by repository path or PR head. |
| [`manifest.rs`](https://github.com/agavra/tuicr/blob/main/manifest.rs) (constants) | Defines the `sessions/` subdirectory name and the lock file for safe concurrent writes. |

## Review Session File Format

Each session is a pretty-printed JSON serialization of the `ReviewSession` struct defined in [`src/model/review.rs`](https://github.com/agavra/tuicr/blob/main/src/model/review.rs). The format captures complete review state for reconstruction:

```json
{
  "id": "e5b8c1d4-a7f2-4b23-9d9e-c9f0a2e5c8e1",
  "repo_path": "/home/user/project",
  "base_commit": "a1b2c3d4",
  "branch_name": "feature/login",
  "diff_source": "WorkingTree",
  "files": {
    "src/main.rs": {
      "reviewed": false,
      "reviewed_hunks": [],
      "file_comments": [],
      "line_comments": {}
    }
  },
  "review_comments": [],
  "pr_session_key": null
}

```

Key fields include:

- **`repo_path`** – Absolute path to the repository root
- **`base_commit`** – The commit SHA used as the review baseline
- **`branch_name`** – Optional branch context
- **`diff_source`** – Either `WorkingTree` or `CommitRange`
- **`files`** – Map of file paths to review flags, hunk selections, comments
- **`pr_session_key`** – For PR reviews, contains the remote, owner, repo, and PR number

## How Filenames Are Determined

In [`storage.rs`](https://github.com/agavra/tuicr/blob/main/storage.rs), the function `relative_path_for_slug` (lines 49-89) computes a deterministic 16-hex-character filename:

- **Local sessions**: Hash combines the slug + canonical repository path, ensuring distinct files for separate checkouts of the same repo
- **PR sessions**: Hash combines the slug + PR head SHA, so each force-push creates a new session file

This design prevents collisions while allowing predictable file locations without manifest lookups for known sessions.

## Core API Operations

### Saving Sessions

`save_session_in_dir_unlocked` writes JSON atomically, updates the manifest, and releases the lock:

```rust
use tuicr::persistence::storage;
use tuicr::model::ReviewSession;
use tuicr::error::Result;

let session: ReviewSession = /* build a ReviewSession */;
let path = storage::save_session(&session)?;   // writes JSON & updates manifest

```

The atomic write mechanism uses `write_atomic`: data is written to a temporary file, then renamed into place. This prevents corruption if the process crashes mid-write.

### Loading Sessions

Direct loading deserializes via `serde_json`:

```rust
let loaded = storage::load_session(&path)?;

```

For context-aware lookup without knowing the exact path:

```rust
let (path, session) = storage::load_latest_session_for_context(
    repo_path,
    Some("main"),
    "a1b2c3d4",
    tuicr::model::SessionDiffSource::WorkingTree,
    None,
)?.expect("session should exist");

```

### Active Session Tracking

The TUI displays open sessions using `mark_session_active`:

```rust
storage::mark_session_active(&session, &path)?;

```

This records the process ID and last-seen timestamp in [`active_sessions.json`](https://github.com/agavra/tuicr/blob/main/active_sessions.json). Stale entries (older than 12 hours) are automatically pruned.

### Conditional Deletion

`delete_session_if_empty` removes sessions only when they contain no comments or reviewed state:

```rust
if storage::delete_session_if_empty(&path)? {
    println!("Empty session removed.");
}

```

## Design Safeguards

The review session persistence layer implements several crash-safety and concurrency mechanisms:

- **Atomic writes** – Temporary file + rename prevents partial writes from corrupting session data
- **Directory-wide lock** – `.tuicr.lock` serializes concurrent writes across processes, with stale-lock detection and automatic cleanup
- **Flat layout** – All files in `sessions/`; manifest provides fast lookups without filesystem traversal
- **Migration support** – `maybe_migrate` detects legacy layouts on first run and restructures data automatically

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`src/persistence/storage.rs`](https://github.com/agavra/tuicr/blob/main/src/persistence/storage.rs) | Persistence API, path hashing, atomic writes, locking, active-session tracking |
| [`src/persistence/manifest.rs`](https://github.com/agavra/tuicr/blob/main/src/persistence/manifest.rs) | Manifest structure ([`index.json`](https://github.com/agavra/tuicr/blob/main/index.json)) and helper functions for lookup, insertion, pruning |
| [`src/model/review.rs`](https://github.com/agavra/tuicr/blob/main/src/model/review.rs) | `ReviewSession` struct and related serializable types |
| [`src/slug.rs`](https://github.com/agavra/tuicr/blob/main/src/slug.rs) | Slug generation for local and PR sessions |

## Summary

- Tuicr stores review sessions as **individual JSON files** in a platform-specific data directory with a **flat `sessions/` layout**
- The **manifest ([`index.json`](https://github.com/agavra/tuicr/blob/main/index.json))** maps slugs to file entries for fast lookup and disambiguation
- **Atomic writes** via temporary files and renames prevent corruption on crashes
- A **directory-wide lock file** (`.tuicr.lock`) serializes concurrent access with stale-lock detection
- **Filenames are deterministic hashes** combining slug + repo path (local) or slug + PR head SHA (PR sessions)
- **Active session tracking** enables the TUI to display currently open reviews with automatic stale entry pruning

## Frequently Asked Questions

### What format does Tuicr use to store review sessions?

Tuicr uses **pretty-printed JSON** to serialize the `ReviewSession` struct. The format includes repository metadata, base commit, diff source, file-level review flags, line-level comments, and optional PR session keys. Files are human-readable and version-controllable if needed.

### How does Tuicr handle concurrent access to session files?

Tuicr implements a **directory-wide locking mechanism** using a `.tuicr.lock` file. Before any write operation, the process acquires this lock; stale locks (from crashed processes) are detected and automatically cleaned up. This ensures only one process modifies sessions at a time while allowing concurrent reads.

### Where are Tuicr review sessions stored on disk?

Sessions are stored in the **platform-specific data directory** under `tuicr/reviews/sessions/`. On Linux this is `~/.local/share/tuicr/reviews/sessions/`, on macOS `~/Library/Application Support/tuicr/reviews/sessions/`, and on Windows `%APPDATA%\tuicr\reviews\sessions\`.

### What happens if Tuicr crashes while saving a session?

The **atomic write mechanism** prevents corruption. Data is written to a temporary file first; only after the write completes successfully is the file renamed to its final location. If the process crashes before the rename, the partial data remains in a temp file and is ignored on next startup.