# How BranchNavigator Switches Between Leaf Contexts in a Pi‑Web Session

> Learn how BranchNavigator switches leaf contexts in a Pi-Web session using a callback-driven flow and RPC navigate_tree command to update the backend cursor.

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

---

**BranchNavigator switches leaf contexts through a callback-driven flow where UI selection triggers an RPC `navigate_tree` command that updates the session's cursor on the backend.**

The **BranchNavigator** component in `agegr/pi-web` enables users to jump between any branch tip (leaf) within an active Pi‑Web session. Understanding this mechanism requires tracing the data flow from UI interaction through React hooks to the RPC layer that coordinates session state.

## The Two-Step Leaf Switching Flow

BranchNavigator does not directly manipulate session state. Instead, it delegates context switching through a decoupled event flow:

1. **UI captures the selection** — Clicking a tree node extracts the target leaf's entry ID
2. **Hook issues the command** — `useAgentSession` detects the ID change and sends a backend command
3. **Backend updates the cursor** — The RPC manager repositions the session and returns fresh context

This separation keeps the navigator component presentational while centralizing session logic in the hook layer.

## Step 1: Capturing Leaf Selection in BranchNavigator

The tree rendering logic in [`components/BranchNavigator.tsx`](https://github.com/agegr/pi-web/blob/main/components/BranchNavigator.tsx) handles user clicks by bubbling the selected leaf's ID upward through the `onLeafChange` callback prop【[`BranchNavigator.tsx`](https://github.com/agegr/pi-web/blob/main/BranchNavigator.tsx) lines 76-78】.

```tsx
// components/BranchNavigator.tsx – Tree node click handling
function TreeNodeView({ node, ..., onSelect }: TreeNodeProps) {
  ...
  onClick={() => onSelect(rep.entry.id)}   // ← leaf ID extracted from entry
}
...
const handleSelect = useCallback((id: string) => {
  onLeafChange(id);                        // Propagate to parent component
}, [onLeafChange]);

```

The `onLeafChange` prop is the sole output contract for this component. It receives a string ID representing any leaf in the branch tree, making the navigator agnostic to what happens after selection.

## Step 2: Reacting to Leaf Changes in useAgentSession

The session management hook [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) wires the leaf change to an actual session transition. It tracks `activeLeafId` in state and uses a `useEffect` to trigger the backend command whenever this value changes【[`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) lines 1443 and 1455】.

```ts
// hooks/useAgentSession.ts – Leaf change side effect
useEffect(() => {
  // When the active leaf changes, tell the agent to jump there
  sendAgentCommand(sessionId, {
    type: "navigate_tree",
    targetId: leafId,                     // leaf ID originating from BranchNavigator
  }).catch(() => {});
}, [sessionId, leafId]);

```

The `navigate_tree` command type is specific to leaf navigation. The hook's dependency array ensures the command fires precisely when either the session or target leaf changes, preventing redundant calls.

## Step 3: Backend Command Processing

The RPC manager at [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) handles the `navigate_tree` command by:

- Validating the target leaf ID exists within the session's branch tree
- Updating the internal session cursor to reference the new entry
- Returning the complete context state for the selected leaf

This backend transition is what actually switches the conversation context. The UI receives the response and re-renders the chat view with messages and state relative to the new leaf position.

## Key Architectural Decisions

The BranchNavigator's leaf switching design reflects several intentional patterns in `agegr/pi-web`:

| Pattern | Implementation | Benefit |
|--------|----------------|---------|
| **Callback props** | `onLeafChange` output only | Component reusability across different session types |
| **Effect-driven commands** | `useEffect` + `sendAgentCommand` | Automatic synchronization without manual trigger |
| **Command pattern** | `navigate_tree` RPC type | Extensible backend protocol for navigation operations |
| **Optimistic UI** | Immediate state update, error catch silent | Responsive feel without blocking on network |

## Summary

- **BranchNavigator** emits leaf selections via the `onLeafChange` callback prop in [`components/BranchNavigator.tsx`](https://github.com/agegr/pi-web/blob/main/components/BranchNavigator.tsx)
- **useAgentSession** listens for `activeLeafId` changes and sends `type: "navigate_tree"` commands via `sendAgentCommand` in [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)
- **lib/rpc-manager.ts** processes the command, updates the session cursor, and returns context that drives the UI re-render
- The decoupled architecture separates presentation (tree UI) from session logic (hook) from state mutation (backend)

## Frequently Asked Questions

### What is a "leaf" in Pi‑Web's BranchNavigator?

A **leaf** is the terminal node at the tip of a branch in Pi‑Web's conversation tree. Each leaf represents a distinct point in the dialogue where the user can continue the conversation from that specific context. The BranchNavigator visualizes the entire tree structure but only emits leaf IDs for selectable endpoints.

### Why does BranchNavigator use a callback instead of directly calling sendAgentCommand?

The callback pattern keeps [`components/BranchNavigator.tsx`](https://github.com/agegr/pi-web/blob/main/components/BranchNavigator.tsx) presentational and framework-agnostic. By accepting `onLeafChange` as a prop, the same component can work with different session implementations—React hooks, direct API calls, or even test mocks—without internal code changes. This separation of concerns follows the container/presenter pattern common in React applications.

### What happens if the navigate_tree command fails?

The `useEffect` in [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) includes a `.catch(() => {})` handler that silently suppresses errors【line 1455】. This optimistic approach assumes the command succeeds while allowing the UI to remain responsive. In production, you may want to add error handling to revert the `activeLeafId` state or display a notification to the user.

### Can BranchNavigator switch to non-leaf nodes?

Based on the implementation, `onSelect` specifically passes `rep.entry.id` from leaf representations. The `navigate_tree` command and the backend cursor update are designed for leaf destinations. Internal branch nodes serve structural purposes in the tree UI but do not appear to be valid targets for session context switching in the current `agegr/pi-web` architecture.