How Herdr Session Persistence Restores Panes: Complete Restoration Pipeline Explained

Herdr restores panes by deserializing a JSON snapshot from disk, remapping legacy pane IDs to new globally unique identifiers, and reconstructing TerminalRuntimes that optionally replay scrollback history or resume agent sessions.

When resuming work in the ogulcancelik/herdr terminal workspace manager, the application reconstructs your complete pane topology from persistent storage. The herdr session persistence restore panes mechanism operates through a coordinated pipeline across src/persist/io.rs, src/persist/restore.rs, and src/app/mod.rs, ensuring that working directories, agent states, and scrollback buffers survive system restarts.

The Three-Stage Restoration Architecture

The restoration process follows a strict sequence to recover the UI state. First, crate::persist::load() reads ~/.config/herdr/session.json and deserializes it into a SessionSnapshot. Second, if experimental pane history is enabled, crate::persist::load_history() ingests the companion session-history.json file. Third, crate::persist::restore() in src/persist/restore.rs orchestrates the reconstruction, accepting the snapshots along with terminal dimensions, shell configuration, and runtime options to regenerate the workspace objects.

Loading and Validating Persisted State

The persistence layer begins its work in src/persist/io.rs, where the on-disk JSON is parsed into structured snapshot types. The SessionSnapshot captures the complete workspace hierarchy including tabs, pane layouts, and metadata. When history persistence is active, a separate SessionHistorySnapshot contains the ANSI scrollback buffers for each pane. These structures serve as the immutable blueprint for the restoration process, allowing the system to reconstruct the terminal state without relying on live process restoration.

Remapping Pane IDs and Resolving Context

Inside restore(), the system first calls restore_node_remapped() at src/persist/restore.rs#L1000-L1007 to walk the saved LayoutSnapshot and allocate brand-new PaneId values. This remapping returns a HashMap<u32, PaneId> that guarantees every restored pane receives a globally unique identifier, preventing collisions with any existing runtime state.

For each pane, the restoration logic resolves the working directory at src/persist/restore.rs#L3838-L3898. If the saved cwd path no longer exists, Herdr falls back to $HOME or /, logging a warning via the warn! macro. Simultaneously, the system reapplies saved manual labels, launch arguments, and agent names to the newly instantiated TerminalState structures (src/persist/restore.rs#L4778-L4804).

Agent Session and Terminal Runtime Restoration

The pane_restore_startup() function at src/persist/restore.rs#L7600-L7638 handles AgentResumePlan creation. When the user has enabled resume_agents_on_restore and the persisted PaneAgentSessionSnapshot remains resumable, Herdr prepares to respawn the agent process rather than launching a standard shell. Duplicate native agent sessions are suppressed through an internal deduplication set.

Terminal spawning occurs at src/persist/restore.rs#L7020-L7070 through three distinct paths:

  • Imported hand-off runtimes are re-attached directly if present in the snapshot.
  • Agent resume plans trigger TerminalRuntime::spawn_agent_restore(), which starts the agent process and marks the terminal to respawn a shell upon agent exit.
  • Standard restoration invokes TerminalRuntime::spawn_with_initial_history(), launching a fresh shell and seeding it with initial_history_ansi when scrollback persistence is available.

Workspace Reconstruction and Layout Pruning

After pane creation, the restoration pipeline prunes orphaned layout nodes via prune_restored_node at src/persist/restore.rs#L5450-L5490, removing any containers that lost their constituent panes during the remapping phase. The focus state and root-pane references are then resolved against the new ID map, with automatic fallback to the first surviving pane when the original focus target no longer exists.

Finally, the restored tabs, terminals, and runtimes are collected into Workspace structs at src/persist/restore.rs#L8910-L8945, preserving workspace IDs, custom names, worktree membership, and the active tab state. This collection replaces the empty initial application state when triggered from the entry point in src/app/mod.rs.

Implementation Example

The following pattern demonstrates how to invoke the restoration pipeline programmatically:

use herdr::persist::{load, load_history, restore};
use herdr::config::ShellModeConfig;
use tokio::sync::mpsc;
use std::sync::{Arc, atomic::AtomicBool};
use tokio::sync::Notify;

// Load persisted snapshots
let snapshot = load().expect("session file not found");
let history = load_history();

// Async runtime plumbing
let (event_tx, _event_rx) = mpsc::channel(4);
let render_notify = Arc::new(Notify::new());
let render_dirty = Arc::new(AtomicBool::new(false));

// Execute restoration
let (workspaces, terminals, runtimes) = restore(
    &snapshot,
    history.as_ref(),
    24,                              // terminal rows
    80,                              // terminal cols
    4 * 1024 * 1024,                 // 4 MiB scrollback limit
    "/bin/bash",
    ShellModeConfig::NonLogin,
    true,                            // resume agents on restore
    event_tx,
    render_notify,
    render_dirty,
);

Within the restoration flow, scrollback seeding occurs when no native agent resume is required:

let startup = pane_restore_startup(saved_agent_session, saved_history, &mut agent_restore);
// startup.initial_history_ansi contains captured ANSI scrollback for replay

Summary

  • Herdr session persistence restore panes relies on SessionSnapshot deserialization from ~/.config/herdr/session.json to reconstruct the workspace topology.
  • restore_node_remapped() in src/persist/restore.rs generates new globally unique PaneId values to avoid runtime collisions.
  • Working directory resolution includes automatic fallback to $HOME or / when the original path is unreachable.
  • The AgentResumePlan system optionally respawns persistent agents instead of launching new shells.
  • TerminalRuntime::spawn_with_initial_history() replays saved ANSI scrollback into restored panes when history persistence is enabled.
  • The entry point in src/app/mod.rs coordinates the entire pipeline, replacing the initial empty state with fully reconstructed workspaces.

Frequently Asked Questions

Where does Herdr store session persistence data?

Herdr persists session state to ~/.config/herdr/session.json and optional pane history to session-history.json. The src/persist/io.rs module handles all disk I/O for these files, including symlink resolution and version compatibility checks.

How does Herdr handle missing working directories when restoring panes?

When the saved working directory no longer exists, Herdr falls back to $HOME or the root directory / at src/persist/restore.rs#L3838-L3898. The fallback event is logged as a warning so users can identify broken workspace configurations.

Can Herdr restore running agent sessions?

Yes, when resume_agents_on_restore is enabled in the configuration, Herdr inspects the PaneAgentSessionSnapshot and creates an AgentResumePlan at src/persist/restore.rs#L7600-L7638. This plan respawns the agent process via TerminalRuntime::spawn_agent_restore() and configures the terminal to launch a shell only after the agent exits.

What happens to pane IDs during restoration?

Herdr never reuses persisted pane IDs directly. Instead, restore_node_remapped() in src/persist/restore.rs#L1000-L1007 allocates fresh PaneId values and maintains a mapping hash table. This remapping ensures global uniqueness across the new session while preserving the layout hierarchy from the snapshot.

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 →