> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monolex.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Dirty Tracking Strategies

> Why MonoTerm uses line-based dirty tracking for optimal rendering performance

# Dirty Tracking Strategies

Terminal emulators need to track which parts of the screen have changed to optimize rendering. This page explains the two main approaches and why MonoTerm chose line-based tracking.

## What is Dirty Tracking?

Dirty tracking answers: "What changed since last render?"

```
╔════════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  DIRTY TRACKING PURPOSE                                                ║
║                                                                        ║
║                                                                        ║
║    Without Dirty Tracking:                                             ║
║                                                                        ║
║    Every frame:                                                        ║
║    ┌────────────────────────────────────────────┐                      ║
║    │  Re-render ENTIRE screen                   │                      ║
║    │  Even if only 1 character changed          │                      ║
║    │  120 cols x 40 rows = 4,800 cells          │                      ║
║    └────────────────────────────────────────────┘                      ║
║                                                                        ║
║    = Wasteful, slow                                                    ║
║                                                                        ║
║                                                                        ║
║    With Dirty Tracking:                                                ║
║                                                                        ║
║    Every frame:                                                        ║
║    ┌────────────────────────────────────────────┐                      ║
║    │  Check what changed                        │                      ║
║    │  Re-render ONLY changed parts              │                      ║
║    │  Maybe just 1 line or a few cells          │                      ║
║    └────────────────────────────────────────────┘                      ║
║                                                                        ║
║    = Efficient, fast                                                   ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Two Approaches: Epoch vs Content Comparison

### Epoch (Sequence Number)

Epoch is a monotonically increasing counter that increments whenever a write happens.

```
╔════════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  EPOCH / SEQUENCE NUMBER                                               ║
║                                                                        ║
║                                                                        ║
║    Concept:                                                            ║
║                                                                        ║
║    State changes ──▶ epoch++                                           ║
║                                                                        ║
║    ┌───────────────────┐      ┌───────────────────┐                    ║
║    │  State            │      │  State            │                    ║
║    │  content: "hello" │  ──▶ │  content: "hello" │                    ║
║    │  epoch: 5         │      │  epoch: 6         │                    ║
║    └───────────────────┘      └───────────────────┘                    ║
║           (before write)            (after write, same content)        ║
║                                                                        ║
║                                                                        ║
║    Renderer Logic:                                                     ║
║                                                                        ║
║    if (last_rendered_epoch < current_epoch) {                          ║
║        // Something changed, need to render                            ║
║        render();                                                       ║
║        last_rendered_epoch = current_epoch;                            ║
║    }                                                                   ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

### Content Comparison

Content comparison examines the actual data. Same content = no change detected.

```
╔════════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  CONTENT-BASED TRACKING                                                ║
║                                                                        ║
║                                                                        ║
║    Concept:                                                            ║
║                                                                        ║
║    State changes ──▶ compare(old, new)                                 ║
║                                                                        ║
║    ┌───────────────────┐      ┌───────────────────┐                    ║
║    │  State            │      │  State            │                    ║
║    │  content: "hello" │  ──▶ │  content: "hello" │                    ║
║    └───────────────────┘      └───────────────────┘                    ║
║           (before write)            (after write, same content)        ║
║                                                                        ║
║                                 Content unchanged! (same content)      ║
║                                                                        ║
║                                                                        ║
║    Renderer Logic:                                                     ║
║                                                                        ║
║    if (old_content != new_content) {                                   ║
║        // Content actually changed, need to render                     ║
║        render();                                                       ║
║    }                                                                   ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## The Critical Difference

The key difference: what happens when the SAME content is written again.

```
╔════════════════════════════════════════════════════════════════════════╗
║  EPOCH: SAME CONTENT WRITTEN AGAIN                                     ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Scenario: Line 2 written with same content                          ║
║                                                                        ║
║                                                                        ║
║       Line 0: "hello"                                                  ║
║       Line 1: "world"                                                  ║
║       Line 2: "foo"    <──  "foo" written again (same content!)        ║
║       Line 3: "test"                                                   ║
║       Line 4: "done"                                                   ║
║                                                                        ║
║                                                                        ║
║    Epoch Behavior:                                                     ║
║                                                                        ║
║       Line 2 epoch: 5 ──▶ 6  (incremented because write happened)      ║
║                                                                        ║
║       Renderer sees: "epoch changed (5->6), must re-render"            ║
║                                                                        ║
║       Result: RE-RENDER (even though content is identical)             ║
║                                                                        ║
║       X  Wasteful                                                      ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝

╔════════════════════════════════════════════════════════════════════════╗
║  CONTENT: SAME CONTENT WRITTEN AGAIN                                   ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Same Scenario: Line 2 written with same content                     ║
║                                                                        ║
║                                                                        ║
║       Line 0: "hello"                                                  ║
║       Line 1: "world"                                                  ║
║       Line 2: "foo"    <──  "foo" written ──▶ still "foo" (same!)      ║
║       Line 3: "test"                                                   ║
║       Line 4: "done"                                                   ║
║                                                                        ║
║                                                                        ║
║    Content Behavior:                                                   ║
║                                                                        ║
║       Line 2: "foo" ──▶ "foo"  (unchanged because content is same)     ║
║                                                                        ║
║       Renderer sees: "content unchanged, skip"                         ║
║                                                                        ║
║       Result: SKIP (correctly identifies no change)                    ║
║                                                                        ║
║       OK Efficient                                                     ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## The Fundamental Distinction: Actions vs Content

```
╔════════════════════════════════════════════════════════════════════════╗
║  WHAT EPOCH VS CONTENT TRACKING ACTUALLY TRACKS                        ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    EPOCH                              CONTENT                          ║
║    =====                              =======                          ║
║                                                                        ║
║    Question asked:                    Question asked:                  ║
║    "Did a write happen?"              "Is the content different?"      ║
║                                                                        ║
║    On write():                        On write():                      ║
║    ──▶ epoch++                        ──▶ compare old vs new           ║
║                                                                        ║
║    Does it look at content?           Does it look at content?         ║
║    ──▶ NO                             ──▶ YES                          ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Why This Matters for AI CLI

AI CLI applications have a specific output pattern where content-based tracking excels.

```
╔════════════════════════════════════════════════════════════════════════╗
║  AI CLI OUTPUT PATTERN                                                 ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Typical AI CLI (Claude Code, Aider, etc.):                          ║
║                                                                        ║
║    ┌─────────────────────────────────────────────┐                     ║
║    │ * Thinking...                               │  <── Rewritten often║
║    │ * Thinking...                               │  <── Same content!  ║
║    │ * Thinking...                               │  <── Same content!  ║
║    │                                             │                     ║
║    │ Here is my response...                      │  <── Actual new     ║
║    │ The answer to your question is...           │ <── Actual new      ║
║    └─────────────────────────────────────────────┘                     ║
║                                                                        ║
║                                                                        ║
║    "Thinking..." line is rewritten every ~100ms                        ║
║    But content is IDENTICAL each time                                  ║
║                                                                        ║
║                                                                        ║
║    ┌─────────────────────────────────────────────────────────────┐     ║
║    │  Tracking Method   │  "Thinking..." rewrite  │  Efficiency  │     ║
║    ├────────────────────┼─────────────────────────┼──────────────┤     ║
║    │  Epoch             │  Re-render every time   │  X  Wasteful │     ║
║    │  Content           │  Skip (same content)    │  OK Efficient│     ║
║    └─────────────────────────────────────────────────────────────┘     ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Partial Update: Natural with Content Comparison

Content comparison makes partial updates natural.

```
╔════════════════════════════════════════════════════════════════════════╗
║  EPOCH: PARTIAL UPDATE IS DIFFICULT                                    ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Scenario: 5 lines, only Line 2 actually changed                     ║
║                                                                        ║
║                                                                        ║
║       Line 0: "hello"                                                  ║
║       Line 1: "world"                                                  ║
║       Line 2: "foo"    ──▶   "bar"   (content changed)                 ║
║       Line 3: "test"                                                   ║
║       Line 4: "done"                                                   ║
║                                                                        ║
║                                                                        ║
║    Single Epoch (whole state):                                         ║
║                                                                        ║
║       [Entire State] ─── epoch: 5 ──▶ 6                                ║
║                              │                                         ║
║                              └── "Something changed"                   ║
║                                                                        ║
║       Renderer knows: "epoch changed"                                  ║
║       Renderer does NOT know: "WHAT changed"                           ║
║       Renderer does NOT know: "WHERE changed"                          ║
║                                                                        ║
║       Options:                                                         ║
║       A) Re-render entire screen (wasteful)                            ║
║       B) Implement separate change tracking (complex)                  ║
║                                                                        ║
║       X  Partial update requires extra work                            ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝

╔════════════════════════════════════════════════════════════════════════╗
║  CONTENT: PARTIAL UPDATE IS NATURAL                                    ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Same Scenario: 5 lines, only Line 2 changed                         ║
║                                                                        ║
║                                                                        ║
║    Per-Line Comparison:                                                ║
║                                                                        ║
║       Line 0: "hello"  ──▶  "hello"  (same OK)                         ║
║       Line 1: "world"  ──▶  "world"  (same OK)                         ║
║       Line 2: "foo"    ──▶  "bar"    (DIFFERENT X)  <── Found it!      ║
║       Line 3: "test"   ──▶  "test"   (same OK)                         ║
║       Line 4: "done"   ──▶  "done"   (same OK)                         ║
║                                                                        ║
║                                                                        ║
║       Renderer immediately knows:                                      ║
║       - WHAT changed: Line 2                                           ║
║       - WHERE: Index 2                                                 ║
║                                                                        ║
║       ──▶ DiffHint::Partial { start_row: 2, end_row: 2 }                        ║
║       ──▶ Only Line 2 sent over IPC                                    ║
║       ──▶ Only Line 2 re-rendered                                      ║
║                                                                        ║
║       OK Partial update is built-in                                    ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Why Line-Level is the Sweet Spot

Terminal dirty tracking could be done at different granularities.

```
╔════════════════════════════════════════════════════════════════════════╗
║  GRANULARITY OPTIONS                                                   ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Screen Size: 120 cols x 40 rows = 4,800 cells                       ║
║                                                                        ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────────────────┐  ║
║    │                                                                │  ║
║    │  CELL-LEVEL TRACKING                                           │  ║
║    │                                                                │  ║
║    │  Track each cell individually:                                 │  ║
║    │                                                                │  ║
║    │    4,800 comparisons to make                                   │  ║
║    │                                                                │  ║
║    │    Problem 1: Tracking overhead is massive                     │  ║
║    │    Problem 2: GPU doesn't benefit (see below)                  │  ║
║    │                                                                │  ║
║    │    X  Too granular                                             │  ║
║    │                                                                │  ║
║    └────────────────────────────────────────────────────────────────┘  ║
║                                                                        ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────────────────┐  ║
║    │                                                                │  ║
║    │  SCREEN-LEVEL TRACKING                                         │  ║
║    │                                                                │  ║
║    │  Track entire screen as one unit:                              │  ║
║    │                                                                │  ║
║    │    One comparison for whole screen                             │  ║
║    │                                                                │  ║
║    │    Problem: Any change ──▶ re-render everything                │  ║
║    │    Typing one character ──▶ 4,800 cells re-rendered            │  ║
║    │                                                                │  ║
║    │    X  Too coarse                                               │  ║
║    │                                                                │  ║
║    └────────────────────────────────────────────────────────────────┘  ║
║                                                                        ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────────────────┐  ║
║    │                                                                │  ║
║    │  LINE-LEVEL TRACKING  <── SWEET SPOT                           │  ║
║    │                                                                │  ║
║    │  Track each line:                                              │  ║
║    │                                                                │  ║
║    │    40 lines = 40 comparisons to manage                         │  ║
║    │    Reasonable overhead                                         │  ║
║    │                                                                │  ║
║    │    Typing on line 5 ──▶ only line 5 re-rendered                │  ║
║    │    120 cells instead of 4,800                                  │  ║
║    │                                                                │  ║
║    │    OK Optimal balance                                          │  ║
║    │                                                                │  ║
║    └────────────────────────────────────────────────────────────────┘  ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Why Line-Level Matches GPU Rendering

```
╔════════════════════════════════════════════════════════════════════════╗
║  GPU RENDERING AND LINE-LEVEL DIRTY TRACKING                           ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    Terminal screen layout:                                             ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────┐              ║
║    │ Line 0: $ ls -la                                   │              ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ Line 1: total 48                                   │              ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ Line 2: drwxr-xr-x  5 user staff  160 Jan 15 10:00 │              ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ Line 3: -rw-r--r--  1 user staff  1234 Jan 15 10:01│              ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ ...                                                │              ║
║    └────────────────────────────────────────────────────┘              ║
║                                                                        ║
║                                                                        ║
║    GPU draws in ROWS:                                                  ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────┐              ║
║    │ [Row 0 texture] [Row 0 texture] [Row 0 texture]    │ <─ 1 draw    ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ [Row 1 texture] [Row 1 texture] [Row 1 texture]    │ <─ 1 draw    ║
║    ├────────────────────────────────────────────────────┤              ║
║    │ ...                                                │              ║
║    └────────────────────────────────────────────────────┘              ║
║                                                                        ║
║                                                                        ║
║    Perfect Alignment:                                                  ║
║                                                                        ║
║    - Line dirty ──▶ row needs redraw                                   ║
║    - Line clean ──▶ row skip                                           ║
║    - 1:1 mapping between dirty tracking and GPU units                  ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## MonoTerm's DiffHint System

MonoTerm uses DiffHint to communicate what changed.

```
╔════════════════════════════════════════════════════════════════════════╗
║  DIFFHINT: COMMUNICATING WHAT CHANGED                                  ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    DiffHint enum:                                                      ║
║                                                                        ║
║    ┌────────────────────────────────────────────────────────────────┐  ║
║    │                                                                │  ║
║    │  Full                                                          │  ║
║    │  ────                                                          │  ║
║    │  All lines need to be rendered.                                │  ║
║    │  Used for: Initial render, resize, scroll                      │  ║
║    │                                                                │  ║
║    ├────────────────────────────────────────────────────────────────┤  ║
║    │                                                                │  ║
║    │  Partial { start_row: 2, end_row: 7 }                             │  ║
║    │  ──────────────────────────────────                            │  ║
║    │  Only specified lines changed.                                 │  ║
║    │  Used for: Normal typing, single-line updates                  │  ║
║    │                                                                │  ║
║    ├────────────────────────────────────────────────────────────────┤  ║
║    │                                                                │  ║
║    │  ScrollOnly { delta: -3 }                                      │  ║
║    │  ─────────────────────────                                     │  ║
║    │  Screen scrolled, no content changed.                          │  ║
║    │  Used for: Pure scroll events                                  │  ║
║    │                                                                │  ║
║    ├────────────────────────────────────────────────────────────────┤  ║
║    │                                                                │  ║
║    │  None                                                          │  ║
║    │  ────                                                          │  ║
║    │  Nothing changed (cursor only maybe).                          │  ║
║    │  Used for: Cursor blink, no-op updates                         │  ║
║    │                                                                │  ║
║    └────────────────────────────────────────────────────────────────┘  ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Comparison Summary

```
╔════════════════════════════════════════════════════════════════════════╗
║  TRACKING METHOD COMPARISON                                            ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    ┌────────────────────────────────────────────────────────────────┐  ║
║    │  Aspect              │  Epoch           │  Content             │  ║
║    ├──────────────────────┼──────────────────┼──────────────────────┤  ║
║    │  Tracks              │  "Write event"   │  "Content different" │  ║
║    ├──────────────────────┼──────────────────┼──────────────────────┤  ║
║    │  Same content        │  Re-render X     │  Skip OK             │  ║
║    │  rewritten           │                  │                      │  ║
║    ├──────────────────────┼──────────────────┼──────────────────────┤  ║
║    │  Computation cost    │  None (++)       │  Compare operation   │  ║
║    ├──────────────────────┼──────────────────┼──────────────────────┤  ║
║    │  Partial update      │  Extra tracking  │  Built-in            │  ║
║    │                      │  needed          │                      │  ║
║    └────────────────────────────────────────────────────────────────┘  ║
║                                                                        ║
║                                                                        ║
║    When to use Epoch:                                                  ║
║    - Same-process multi-renderer coordination                          ║
║    - Version tracking (e.g., resize synchronization)                   ║
║    - When write always means content changed                           ║
║                                                                        ║
║    When to use Content Comparison:                                     ║
║    - Cross-boundary (IPC) dirty tracking                               ║
║    - When same content may be rewritten                                ║
║    - When partial updates are desired                                  ║
║    - AI CLI workloads (frequent rewrites)                              ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```

## Summary

```
╔════════════════════════════════════════════════════════════════════════╗
║  DIRTY TRACKING SUMMARY                                                ║
╠════════════════════════════════════════════════════════════════════════╣
║                                                                        ║
║    ┌───────────────────────────────────────────────────────────────┐   ║
║    │                                                               │   ║
║    │  MonoTerm uses LINE-LEVEL CONTENT COMPARISON because:         │   ║
║    │                                                               │   ║
║    │  1. AI CLIs frequently rewrite same content                   │   ║
║    │     ──▶ Content comparison skips redundant work               │   ║
║    │                                                               │   ║
║    │  2. GPU renders in rows                                       │   ║
║    │     ──▶ Line-level matches GPU rendering units                │   ║
║    │                                                               │   ║
║    │  3. Partial updates reduce IPC traffic                        │   ║
║    │     ──▶ Only send changed lines                               │   ║
║    │                                                               │   ║
║    │  4. 40 lines is manageable                                    │   ║
║    │     ──▶ Not too granular, not too coarse                      │   ║
║    │                                                               │   ║
║    └───────────────────────────────────────────────────────────────┘   ║
║                                                                        ║
║                                                                        ║
║    Result:                                                             ║
║                                                                        ║
║    ┌───────────────────────────────────────────────────────────────┐   ║
║    │                                                               │   ║
║    │  Typical typing:                                              │   ║
║    │    Only 1 line dirty                                          │   ║
║    │    ──▶ 1 line sent over IPC                                   │   ║
║    │    ──▶ 1 line re-rendered by GPU                              │   ║
║    │                                                               │   ║
║    │  AI "Thinking..." rewrite:                                    │   ║
║    │    Same content detected                                      │   ║
║    │    ──▶ 0 lines sent over IPC                                  │   ║
║    │    ──▶ 0 lines re-rendered by GPU                             │   ║
║    │                                                               │   ║
║    └───────────────────────────────────────────────────────────────┘   ║
║                                                                        ║
╚════════════════════════════════════════════════════════════════════════╝
```
