# In-Session Branching vs Forking in Pi-Web: How navigate_tree Differs from New Session Files

> Understand in-session branching versus forking new session files in Pi-Web. Learn how navigate_tree updates the leaf pointer unlike creating new conversation histories.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: deep-dive
- Published: 2026-08-16

---

**In-session branching updates the leaf pointer within the same `.jsonl` file, while forking creates an entirely new session file with its own conversation history.**

Pi-Web implements two distinct mechanisms for diverging from the main conversation flow. Understanding how **in-session branching** (`navigate_tree`) differs from **forking new session files** is essential for developers building on the `agegr/pi-web` codebase. Both commands are handled in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts), but they produce fundamentally different storage and lifecycle outcomes.

## What Is In-Session Branching (navigate_tree)?

In-session branching allows users to continue a conversation from any previous point without creating a new file. When a user clicks **Continue** in the Branch Navigator, the front-end dispatches a `navigate_tree` command.

### How navigate_tree Works

The `navigate_tree` command updates the session's internal **leaf pointer** to a different entry in the same conversation tree:

```ts
// Sent from the UI via useAgentSession.ts
await sendAgentCommand(sessionId, {
  type: "navigate_tree",
  targetId: entryId  // entryId of the leaf to continue from
});

```

In [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts), this case simply switches the leaf:

```ts
case "navigate_tree":
  // Switch the current leaf to targetId within the same .jsonl file
  session.setLeafId(cmd.targetId);
  break;

```

### Storage Impact of In-Session Branching

- The **same `.jsonl` file** continues to store all messages
- New `message` entries receive a `parentId` pointing to the selected branch point
- The `AgentSessionWrapper` remains alive in `globalThis.__piSessions`
- File size grows incrementally as new branches are added

## What Is Forking New Session Files?

Forking creates a **completely independent session** starting from the current conversation state. When a user clicks **Fork** on a user message, the back-end generates a fresh session file.

### How Forking Works

The front-end sends a `fork` command:

```ts
// Triggered by the "Fork" button on a user message
await sendAgentCommand(sessionId, {
  type: "fork"
});

```

The back-end handling in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) performs multiple operations:

```ts
case "fork":
  // Create fresh .jsonl file, copy header, destroy old wrapper
  const newId = await session.fork();   // creates new file & wrapper
  this.destroyWrapper(session.id);      // removes old wrapper
  return { newSessionId: newId };

```

### Storage Impact of Forking

- A **new file** is created at `~/.pi/agent/sessions/<timestamp>_<uuid>.jsonl`
- The original session file remains **untouched**
- The new file's header contains `parentSession` metadata pointing to the source (display-only)
- The `systemPrompt` is cleared in the forked session
- The original `AgentSessionWrapper` is destroyed and replaced

## Key Differences: navigate_tree vs Fork

| Aspect | In-Session Branching | Forking |
|--------|----------------------|---------|
| **File created?** | No — same `.jsonl` file | Yes — new `.jsonl` file |
| **Wrapper lifecycle** | Preserved, leaf updated | Destroyed and replaced |
| **Parent metadata** | Unchanged | `parentSession` field added |
| **Session ID** | Unchanged | New UUID generated |
| **UI trigger** | "Continue" in Branch Navigator | "Fork" button on message |
| **Back-end case** | `"navigate_tree"` | `"fork"` |

## Core Files Implementing Both Mechanisms

Understanding the codebase requires familiarity with these locations:

- **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)** — Command router implementing both `"navigate_tree"` and `"fork"` cases
- **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** — Front-end hook dispatching commands to the back-end
- **[`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts)** — Reads `.jsonl` files and normalizes branches for display

## When to Use Each Approach

**Use in-session branching (`navigate_tree`)** when you want to:
- Explore alternate conversation paths within the same saved session
- Keep all variants accessible from a single file
- Minimize disk usage and session management overhead

**Use forking** when you want to:
- Create a permanent split with independent evolution
- Start fresh with a cleared `systemPrompt`
- Preserve the original conversation exactly as it was

## Summary

- **In-session branching** (`navigate_tree`) switches the `leafId` pointer in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) without creating new files — the same `AgentSessionWrapper` persists and new messages attach to alternate parents within the existing `.jsonl` structure.
- **Forking** creates a brand-new session file with its own ID, copies the header with `parentSession` metadata, destroys the original wrapper, and clears the system prompt for a fresh start.
- Both commands are handled in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) but differ fundamentally in storage model, lifecycle, and intended use case.

## Frequently Asked Questions

### Does forking delete the original session file?

No. The original session file remains completely untouched. Forking only creates a new file and updates the in-memory wrapper registry. The `parentSession` metadata in the new file's header is for UI display purposes only and does not link the files at the storage level.

### Can you merge branches back together after using navigate_tree?

No native merge operation exists in the current Pi-Web implementation. Once you branch via `navigate_tree`, each leaf continues independently within the same file. The [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) normalizes branches for display, but there is no mechanism to recombine divergent conversation paths.

### What happens to the system prompt when you fork versus branch?

Forking explicitly clears the `systemPrompt` in the new session file's header. In-session branching preserves all existing session state including the system prompt, since the same file and wrapper continue to be used.