# How to Use Fork and Rewind to Manage Conversation History in Claudian

> Master Claudian's fork and rewind features to manage conversation history non-destructively. Explore alternate dialogue paths without losing your original chat.

- Repository: [YishenTu/claudian](https://github.com/YishenTu/claudian)
- Tags: how-to-guide
- Published: 2026-03-17

---

**Claudian lets you rewind to any previous assistant response or fork a conversation into a new tab, enabling non-destructive exploration of alternate dialogue paths without losing your original chat flow.**

The `YishenTu/claudian` repository implements these features through a coordinated system of UI rendering, context detection, and SDK operations. By leveraging the `findRewindContext` utility and the `ConversationController` layer, you can programmatically navigate conversation history or create parallel discussion branches. This guide explains the complete architecture and provides runnable code examples for implementing both patterns.

## Architecture of Fork and Rewind

Claudian’s conversation history management relies on four distinct layers working in concert. The **UI rendering** layer in [`src/features/chat/rendering/MessageRenderer.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rendering/MessageRenderer.ts) injects **rewind** and **fork** icons into eligible user messages via `MessageRenderer.addRewindButton` and `MessageRenderer.addForkButton`. These buttons call back into the **conversation controller**, which orchestrates the operation.

The **rewind context detection** layer in [`src/features/chat/rewind.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rewind.ts) provides the `findRewindContext` function that validates whether a message can be rewound. The **service layer** in [`src/core/agent/ClaudianService.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/agent/ClaudianService.ts) exposes the actual SDK operations `rewind` and `applyForkState`, while the **tab management** layer in [`src/features/chat/tabs/Tab.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/tabs/Tab.ts) handles fork routing through `handleForkRequest` and `handleForkAll`.

## Detecting Rewind Eligibility

Before displaying controls, Claudian verifies that a user message supports rewinding. The `MessageRenderer.isRewindEligible` method calls `findRewindContext` from [`src/features/chat/rewind.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rewind.ts) to locate the previous assistant UUID and confirm a subsequent response exists.

```typescript
const ctx = findRewindContext(allMessages, index);
return !!ctx.prevAssistantUuid && ctx.hasResponse;

```

The `findRewindContext` function walks backward from the target message to find the most recent assistant message containing a valid `sdkAssistantUuid`. It then scans forward to ensure the user message has an assistant response after it, setting the `hasResponse` flag. Only when both conditions are met does the UI render the rewind and fork buttons.

## Rendering Rewind and Fork Buttons

The `MessageRenderer` class constructs the interface elements programmatically. The `addRewindButton` method creates a span with the class `claudian-message-rewind-btn`, attaches the SVG icon defined in `MessageRenderer.REWIND_ICON`, and wires a click listener to the `rewindCallback`.

```typescript
private addRewindButton(msgEl: HTMLElement, messageId: string) {
  const toolbar = this.getOrCreateActionsToolbar(msgEl);
  const btn = toolbar.createSpan({ cls: 'claudian-message-rewind-btn' });
  btn.innerHTML = MessageRenderer.REWIND_ICON;
  btn.setAttribute('aria-label', t('chat.rewind.ariaLabel'));
  btn.addEventListener('click', async e => {
    e.stopPropagation();
    await this.rewindCallback?.(messageId);
  });
}

```

The `addForkButton` method follows an identical pattern, utilizing `MessageRenderer.FORK_ICON` and the `forkRequestCallback`. These callbacks are injected during tab initialization in `Tab.initializeTabControllers`, binding the UI to `ConversationController.rewind` and `handleForkRequest` respectively.

## Executing a Rewind Operation

When a user clicks rewind, `ConversationController.rewind` in [`src/core/controllers/ConversationController.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/controllers/ConversationController.ts) validates the context, prompts for confirmation, and invokes the service layer.

```typescript
const result = await this.service.rewind(userMsg.sdkUserUuid, prevAssistantUuid);

```

The `ClaudianService.rewind` method in [`src/core/agent/ClaudianService.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/agent/ClaudianService.ts) forwards the `sdkUserUuid` and `prevAssistantUuid` to the Anthropic SDK. Upon receiving a `RewindFilesResult`, the controller writes the restored files to the vault and displays a success notice via `t('chat.rewind.notice')`. Errors surface through Obsidian notices using `t('chat.rewind.failed')`.

## Forking Conversations to New Tabs

Forking diverges conversation history by creating a new session branch. The process begins in [`src/features/chat/tabs/Tab.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/tabs/Tab.ts) where `handleForkRequest` validates the streaming state, resolves the source session via `resolveForkSource`, and constructs a `ForkContext` object.

The `ForkContext` includes the trimmed message history up to the user message, the `sourceSessionId`, the `resumeAt` assistant UUID, and metadata like the current note title. If the user initiated the fork through the UI, `ForkTargetModal` in [`src/shared/modals/ForkTargetModal.ts`](https://github.com/YishenTu/claudian/blob/main/src/shared/modals/ForkTargetModal.ts) prompts whether to open the fork in a **new** tab or the **current** tab.

```typescript
await forkRequestCallback({
  messages: deepCloneMessages(msgs.slice(0, userIdx)),
  sourceSessionId: source.sourceSessionId,
  resumeAt: rewindCtx.prevAssistantUuid,
  sourceTitle: source.sourceTitle,
  forkAtUserMessage: countUserMessagesForForkTitle(...),
  currentNote: source.currentNote,
});

```

The receiving callback creates a fresh `Tab` instance and calls `ClaudianService.applyForkState` to seed the new SDK session with the fork metadata, effectively cloning the conversation state up to the selected point.

## Code Examples

### Programmatically Rewind a Specific Message

Use this pattern to trigger a rewind from a custom command or automation:

```typescript
import { findRewindContext } from 'src/features/chat/rewind';
import { ConversationController } from 'src/core/controllers/ConversationController';

async function rewindMessage(controller: ConversationController, userMessageId: string) {
  const msg = controller.state.messages.find(m => m.id === userMessageId);
  if (!msg?.sdkUserUuid) {
    new Notice('Message not rewound – missing SDK UUID');
    return;
  }

  const ctx = findRewindContext(
    controller.state.messages, 
    controller.state.messages.indexOf(msg)
  );
  
  if (!ctx.prevAssistantUuid || !ctx.hasResponse) {
    new Notice('No rewind target found');
    return;
  }

  if (!await confirm('Rewind to this point?')) return;

  const result = await controller.service.rewind(
    msg.sdkUserUuid, 
    ctx.prevAssistantUuid
  );
  console.log('Rewind completed', result);
}

```

### Fork the Current Conversation into a New Tab

Implement a custom fork workflow that prompts for the target location:

```typescript
import { Tab, createTab } from 'src/features/chat/tabs/Tab';
import { ForkTargetModal } from 'src/shared/modals/ForkTargetModal';

async function forkCurrentConversation(tab: Tab) {
  const target = await new ForkTargetModal(app).open();
  if (!target) return;

  const forkCtx = await tab.controllers.conversationController!.makeForkContextForAll();

  if (target === 'new-tab') {
    const newTab = createTab({
      plugin,
      mcpManager,
      containerEl: app.workspace.getLeaf().containerEl,
    });
    await newTab.service?.applyForkState(forkCtx);
  } else {
    await tab.service?.applyForkState(forkCtx);
  }
}

```

### UI Structure for Action Buttons

The buttons render inside each eligible user message container:

```html
<div class="claudian-user-msg-actions">
  <span class="claudian-message-rewind-btn" aria-label="Rewind"></span>
  <span class="claudian-message-fork-btn" aria-label="Fork"></span>
</div>

```

These elements receive their icons from `MessageRenderer.REWIND_ICON` and `MessageRenderer.FORK_ICON` constants.

## Summary

- **Rewind eligibility** is determined by `findRewindContext` in [`src/features/chat/rewind.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rewind.ts), which verifies the presence of a previous assistant UUID and a subsequent response.
- **UI controls** are injected by `MessageRenderer` in [`src/features/chat/rendering/MessageRenderer.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rendering/MessageRenderer.ts) using `addRewindButton` and `addForkButton`.
- **Rewind execution** flows through `ConversationController.rewind` to `ClaudianService.rewind`, restoring files via the Anthropic SDK.
- **Forking** creates a `ForkContext` via `handleForkRequest` in [`src/features/chat/tabs/Tab.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/tabs/Tab.ts), allowing exploration in new or current tabs through `applyForkState`.

## Frequently Asked Questions

### How does Claudian determine which messages can be rewound?

Claudian calls `findRewindContext` from [`src/features/chat/rewind.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/rewind.ts) to scan backward for the nearest assistant message with a valid `sdkAssistantUuid` and forward to confirm an assistant response exists after the user message. Only messages meeting both criteria display the rewind button.

### What happens to file changes when I rewind a conversation?

When `ConversationController.rewind` executes successfully, it receives a `RewindFilesResult` from the Anthropic SDK and writes the restored file state to your vault. The controller surfaces success or failure through Obsidian notices defined in the i18n keys `chat.rewind.notice` and `chat.rewind.failed`.

### Can I fork a conversation without creating a new tab?

Yes. The `ForkTargetModal` in [`src/shared/modals/ForkTargetModal.ts`](https://github.com/YishenTu/claudian/blob/main/src/shared/modals/ForkTargetModal.ts) prompts you to choose between a **new** tab or the **current** tab. Selecting the current tab calls `applyForkState` on the existing `Tab` instance, replacing its conversation history with the forked context while preserving the UI container.

### What is included in a ForkContext object?

The `ForkContext` contains the truncated message array up to the fork point, the `sourceSessionId` (live or persisted), the `resumeAt` assistant UUID where the new session continues, the original conversation title, and metadata about the current note. This structure is passed to `ClaudianService.applyForkState` to initialize the branched session.