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

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, 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 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 Core API for saving, loading, deleting, and marking sessions as active. Handles atomic writes, directory-wide locking, and migration from older layouts.
manifest.rs Maintains 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 (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. The format captures complete review state for reconstruction:

{
  "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, 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:

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:

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

For context-aware lookup without knowing the exact path:

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:

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

This records the process ID and last-seen timestamp in 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:

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 Persistence API, path hashing, atomic writes, locking, active-session tracking
src/persistence/manifest.rs Manifest structure (index.json) and helper functions for lookup, insertion, pruning
src/model/review.rs ReviewSession struct and related serializable types
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) 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.

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 →