# Tree Data Model & Algorithms

This document details how conversation versions and turns are represented, branched, and visualized in `dsh-plugin-message-edit`.

---

## 1. Dual-Level Representation

There are two distinct levels of data representation in the system:

1. **Storage Level (Session DAG)**:
   - DSH enforces session-level isolation. Each branch is a distinct DSH session record with `parentSession`, `isSeeded`, and a separate `inheritedEventCount`.
   - The host maintains durable `message-tree/version` markers detailing which turn was edited/retried and what changed.

2. **Presentation Level (Turn-Level Branching Tree)**:
   - A user thinks of conversation branching at the **message/turn** level, not the session container level.
   - `buildTurnTree` projects the session versions into individual turn nodes.

```
Session DAG (Storage):
Session A (Original)  ──[edit turn 1]──>  Session B (Fork)

Turn Tree (Visualization):
               [Root Conversation]
                 /             \
        [A: Turn 1 (1/2)]    [B: Turn 1 (2/2) - Edited]
               |                    |
        [A: Turn 2]          [B: Turn 2]
```

---

## 2. Core Algorithms

### 2.1 Turn Tree Construction (`buildTurnTree`)
*Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L173), [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L415)*

Transforms `versions` into an array of turn nodes:
1. **Root Conversation Node (`${rootSessionId}#root`)**: Represents the origin anchor of the conversation.
2. **Root Session Turns**:
   - Turn 1 hangs off `${rootSessionId}#root`.
   - Turn $k$ ($k > 1$) hangs off `${rootSessionId}#t${k-1}`.
3. **Forked Session Turns**:
   - For a session branched at `targetTurn = T`:
     - If $T = 1$: Turn 1 hangs off `${rootSessionId}#root` (sibling of the original Turn 1).
     - If $T > 1$: Turn $T$ hangs off `${parentSessionId}#t${T-1}` (sibling of parent's Turn $T$).
     - Subsequent turns $T+1, T+2, \dots$ hang off the previous turn in the same session (`${sessionId}#t${k-1}`).
4. **Safety Fallback**: Any node whose computed `parentId` does not exist in the graph is automatically attached to `${rootSessionId}#root`, preventing disconnected subtrees.

### 2.2 Sibling Fan-Out (`attachParentId`)
*Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L9)*

When a user edits Turn 1 repeatedly (e.g. Turn 1 $\rightarrow$ Edit 1 $\rightarrow$ Edit 2 while viewing Edit 1):
- Without fan-out, edits form a chain: $A \rightarrow B \rightarrow C$.
- `attachParentId` traverses up versions of the same turn and stops at the first session that is *not* an edit of that turn ($A$).
- Result: Both Edit 1 and Edit 2 hang off $A$ as sibling branches.

### 2.3 Ghost Ancestor Recovery (`ancestorChainFromLog` & `collectFamily`)
*Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L110-L171)*

If an intermediate session in a family is deleted by the user in DSH:
- The deleted session's own event log is gone.
- However, its descendant sessions inherited its prefix log (including the `message-tree/version` marker describing the deleted parent).
- `ancestorChainFromLog` inspects the surviving descendant's seed events to reconstruct deleted ancestors as **ghost nodes** (`deleted: true`).
- `collectFamily` ensures the family graph remains fully connected even when intermediate nodes are deleted.

### 2.4 Active Path Calculation
*Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L318-L339)*

To highlight only the active branch path without highlighting superseded sibling branches:
1. Locate the latest turn node in `currentSessionId`.
2. Walk upwards following `parentId` pointers until reaching `${rootSessionId}#root`.
3. Mark only nodes on this walk with `onCurrentPath = true`.

### 2.5 Bubble Version Ring (`ringFor`)
*Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L30-L68)*

Calculates the `‹ n/m ›` counter under a message at `turn` while viewing `sessionId`:
- Walks parent links to find the common fork point for that turn.
- Filters out deleted/ghost sessions (renumbering over surviving versions).
- Returns `{ alternatives, index }`. If fewer than 2 alternatives exist, returns `null` (counter is hidden).

---

## 3. Graph Layout & Springs

*Location: [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L567-L612)*

- **Tidy Tree Layout (`layoutTurnTree`)**:
  - Leaf nodes take successive horizontal slots (`cursor * SLOT_X`, where `SLOT_X = 206px`).
  - Parent nodes center horizontally over their children (`(min_x + max_x) / 2`).
  - Depths scale vertically (`depth * SLOT_Y`, where `SLOT_Y = 132px`).
- **Spring Physics (`springs.current`)**:
  - Cards smoothly animate to their target coordinates using critically-damped spring equations ($k = 190, c = 24$).
  - New cards spawn at their parent's coordinates and spring outward.
  - Edges are rendered as cubic SVG bezier curves connecting parent card bottoms to child card tops.
