Skip to main content

State Absorber: The Key Component

The State Absorber overwrites instead of queues. This single design choice guarantees both memory stability and temporal consistency.

Position in Pipeline

╔═════════════════════════════════════════════════════════════════════╗
║  STATE ABSORBER: THE BRIDGE                                         ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  PTY Daemon ──▶ Actor ──▶ GridWorker ──▶ [State Absorber]           ║
║                                               │                     ║
║                                               ▼                     ║
║                                          ACK Gate ──▶ Frontend      ║
║                                                                     ║
║  ─────────────────────────────────────────────────────────────────  ║
║                                                                     ║
║  ABSORBER ROLE:                                                     ║
║                                                                     ║
║  • Receives ALL data (every PTY update)                             ║
║  • Stores CURRENT state only                                        ║
║  • Never blocks upstream                                            ║
║  • Fixed memory footprint                                           ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Without vs With State Absorber

╔═════════════════════════════════════════════════════════════════════╗
║  WITHOUT STATE ABSORBER                                             ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  PTY (1000/sec) ──▶ Actor ──▶ Queue ──▶ ACK (60/sec) ──▶ Frontend   ║
║                                 │                                   ║
║                                 └── Queue grows: 940 items/sec      ║
║                                     Memory grows indefinitely       ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

╔═════════════════════════════════════════════════════════════════════╗
║  WITH STATE ABSORBER                                                ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  PTY (1000/sec) ──▶ Actor ──▶ Absorber ──▶ ACK (60/sec)             ║
║                                 │                                   ║
║                                 └── Always ONE state, overwrites    ║
║                                     Memory fixed                    ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Overwrite vs Queue

╔═════════════════════════════════════════════════════════════════════╗
║  QUEUE APPROACH (Traditional)                                       ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Input:  S1 ──▶ S2 ──▶ S3 ──▶ S4 ──▶ S5 ──▶ ... ──▶ S10            ║
║                                                                     ║
║  Queue:  [S1][S2][S3][S4][S5]...[S10]  ◀── ALL states kept          ║
║                                                                     ║
║  Output: S1 ──▶ S2 ──▶ S3 ──▶ ...  (one at a time)                  ║
║                                                                     ║
║  Memory: Grows with queue depth (unbounded)                         ║
║  Time:   User sees OLD states (queue backlog)                       ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

╔═════════════════════════════════════════════════════════════════════╗
║  OVERWRITE APPROACH (State Absorber)                                ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Input:  S1 ──▶ S2 ──▶ S3 ──▶ S4 ──▶ S5 ──▶ ... ──▶ S10            ║
║          │     │     │     │     │            │                     ║
║          ▼     ▼     ▼     ▼     ▼            ▼                     ║
║                                                                     ║
║  State:  [S1]                                                       ║
║          [S2] ◀── overwrites S1                                     ║
║          [S3] ◀── overwrites S2                                     ║
║          ...                                                        ║
║          [S10] ◀── only CURRENT state exists                        ║
║                                                                     ║
║  Output: S10 (when ACK allows)                                      ║
║                                                                     ║
║  Memory: Fixed (viewport size only)                                 ║
║  Time:   User sees CURRENT state (no backlog)                       ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Key Difference

ApproachPhilosophy
Queue”Remember everything, show in order”
Overwrite”Remember current, show current”
For terminals, CURRENT is what matters. Intermediate states are noise.

Memory Comparison

╔═════════════════════════════════════════════════════════════════════╗
║  MEMORY USAGE: QUEUE vs ABSORBER                                    ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Typical Terminal: 120 cols x 40 rows = 4,800 cells                 ║
║  Cell size: ~17 bytes (char + colors + flags)                       ║
║  Viewport memory: 4,800 x 17 = ~80KB                                ║
║                                                                     ║
║  ─────────────────────────────────────────────────────────────────  ║
║                                                                     ║
║  Queue-Based (1 minute of AI output):                               ║
║  • 1000 states/sec x 60 sec = 60,000 states queued                  ║
║  • 60,000 x 80KB = 4.8GB memory                                     ║
║                                                                     ║
║  State Absorber (same scenario):                                    ║
║  • 1 state (current)                                                ║
║  • 1 x 80KB = 80KB memory                                           ║
║                                                                     ║
║  Difference: 60,000x less memory                                    ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Memory Control Across Pipeline

╔═════════════════════════════════════════════════════════════════════╗
║  WHERE MEMORY IS CONTROLLED                                         ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  LAYER 1: PTY DAEMON                                                ║
║  ─────────────────────────────────────────────────────────────────  ║
║  Memory Control: Unix Socket backpressure                           ║
║  • Kernel ~208KB limit                                              ║
║  • When socket full, write() blocks                                 ║
║  • Rarely triggered (Tauri keeps reading)                           ║
║  Growth: Theoretically yes, practically no                          ║
║                                                                     ║
║  LAYER 2: TAURI BACKEND                                             ║
║  ─────────────────────────────────────────────────────────────────  ║
║  Memory Control: State Absorber OVERWRITE                           ║
║  • Grid: Fixed (rows x cols x cell_size)                            ║
║  • New data overwrites old                                          ║
║  • No accumulation possible                                         ║
║  Growth: IMPOSSIBLE                                                 ║
║                                                                     ║
║  LAYER 3: FRONTEND                                                  ║
║  ─────────────────────────────────────────────────────────────────  ║
║  Memory Control: ACK rate limiting                                  ║
║  • Receives max 60 states/sec (ACK-gated)                           ║
║  • Each state overwrites in xterm.js                                ║
║  • No event queue buildup                                           ║
║  Growth: IMPOSSIBLE (ACK prevents overflow)                         ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Pipeline Never Blocks

╔═════════════════════════════════════════════════════════════════════╗
║  SCENARIO: Renderer is slow (waiting for ACK)                       ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  T=0ms    PTY: output "Line 1"                                      ║
║           Actor: process ──▶ GridWorker                             ║
║           State Absorber: store "Line 1"                            ║
║           ACK: waiting_ack=false ──▶ emit, waiting_ack=true         ║
║                                                                     ║
║  T=5ms    PTY: output "Line 2"                                      ║
║           Actor: process ──▶ GridWorker                             ║
║           State Absorber: OVERWRITE with "Line 1 + Line 2"          ║
║           ACK: waiting_ack=true ──▶ NO emit (absorbed)              ║
║                                                                     ║
║  T=10ms   PTY: output "Line 3"                                      ║
║           Actor: process ──▶ GridWorker                             ║
║           State Absorber: OVERWRITE with all 3 lines                ║
║           ACK: waiting_ack=true ──▶ NO emit (absorbed)              ║
║                                                                     ║
║  ...PTY keeps outputting, State Absorber keeps absorbing...         ║
║                                                                     ║
║  T=16ms   Frontend: finished render, invoke("grid_ack")             ║
║           ACK: waiting_ack=false                                    ║
║           State Absorber: has latest state ready                    ║
║           ACK: emit(latest), waiting_ack=true                       ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Key Observation

╔═════════════════════════════════════════════════════════════════════╗
║  DECOUPLED PROCESSING AND RENDERING                                 ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  • PTY NEVER waits for renderer                                     ║
║  • Actor NEVER waits for ACK                                        ║
║  • State Absorber ALWAYS accepts new data                           ║
║  • ACK ONLY controls emission, not processing                       ║
║                                                                     ║
║  The pipeline is DECOUPLED:                                         ║
║                                                                     ║
║  [PTY ──▶ Actor ──▶ Absorber]  ◀──▶  [ACK ──▶ Frontend]             ║
║         PROCESSING                    RENDERING                     ║
║        (always fast)                 (may be slow)                  ║
║                                                                     ║
║  State Absorber is the decoupling point.                            ║
║  Unlike a queue, it does not grow.                                  ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

State Coalescing (Not Data Loss)

╔═════════════════════════════════════════════════════════════════════╗
║  IMPORTANT DISTINCTION                                              ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  DATA LOSS:                                                         ║
║  Input bytes are dropped, never processed                           ║
║  Result: Missing content, corrupted output                          ║
║                                                                     ║
║  STATE COALESCING:                                                  ║
║  All bytes processed, intermediate STATES discarded                 ║
║  Result: Final content preserved, intermediate views skipped        ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Example

PTY Output: “H” -> “He” -> “Hel” -> “Hell” -> “Hello”
╔═════════════════════════════════════════════════════════════════════╗
║  DATA LOSS (BAD)                                                    ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  • Process: "H", drop "e", drop "l", process "l", process "o"       ║
║  • Final: "Hlo"  ◀── WRONG, content corrupted                       ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

╔═════════════════════════════════════════════════════════════════════╗
║  STATE COALESCING (State Absorber)                                  ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  • Process ALL: "H" ──▶ "He" ──▶ "Hel" ──▶ "Hell" ──▶ "Hello"       ║
║  • Store: each state overwrites previous                            ║
║  • Emit: only "Hello" (when ACK allows)                             ║
║  • Final: "Hello"  ◀── CORRECT, content preserved                   ║
║                                                                     ║
║  User did not see "H", "He", "Hel", "Hell" but WHO CARES?           ║
║  They see "Hello" which is what matters.                            ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Data Flow Verification

╔═════════════════════════════════════════════════════════════════════╗
║  NO DATA LOSS AT ANY POINT                                          ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  PTY bytes ──▶ Alacritty VTE Parser ──▶ Grid cells ──▶ Absorber     ║
║                      │                                   │          ║
║                      │                                   │          ║
║              ALL bytes parsed                    State overwrite    ║
║              (escape sequences,                  (only latest       ║
║               UTF-8, control codes)               kept)             ║
║                                                                     ║
║  No data loss at any point. Only STATE coalescing.                  ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

The Dual Guarantee

ONE mechanism (overwrite) provides TWO guarantees:
╔═════════════════════════════════════════════════════════════════════╗
║  1. MEMORY STABILITY                                                ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Queue:       [S1][S2][S3]...[S1000] ──▶ Memory grows               ║
║  Overwrite:   [S1000]                ──▶ Memory fixed               ║
║                                                                     ║
║  Do not accumulate ──▶ Memory does not grow                         ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

╔═════════════════════════════════════════════════════════════════════╗
║  2. TEMPORAL CONSISTENCY                                            ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Queue:       User sees S1, S2, S3... (past states)                 ║
║  Overwrite:   User sees S1000 (current state)                       ║
║                                                                     ║
║  Do not accumulate ──▶ User sees present, not past                  ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝
SMPC Applied: One simple design choice (overwrite vs queue) solves TWO problems. This is the essence of SMPC: simplicity that handles multiple concerns.

Visual: Memory + Time Over Duration

╔═════════════════════════════════════════════════════════════════════╗
║  MEMORY & FRAME CONTENT                                             ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Scenario: 10,000 state updates, renderer at its pace               ║
║                                                                     ║
║  MEMORY USAGE                    FRAME CONTENT                      ║
║    │                                                                ║
║    │    Queue   /                Queue Frame:                       ║
║    │          /                  ┌─────┬─────┬─────┬─────┐          ║
║    │        /                    │ T=0 │ T=1 │ T=2 │ T=3 │          ║
║    │      /                      ├─────┼─────┼─────┼─────┤          ║
║    │    /                        │ S0  │ S50 │S100 │S150 │ past     ║
║    │  /                          └─────┴─────┴─────┴─────┘          ║
║    │/                            (showing old from queue)           ║
║    │                                                                ║
║    │══════════ Absorber         Absorber Frame:                     ║
║    │                             ┌─────┬─────┬─────┬─────┐          ║
║    │                             │ T=0 │ T=1 │ T=2 │ T=3 │          ║
║    │                             ├─────┼─────┼─────┼─────┤          ║
║    │                             │ now │ now │ now │ now │ curr     ║
║    └────────────────▶ Time       └─────┴─────┴─────┴─────┘          ║
║                                  (always showing current)           ║
║                                                                     ║
║  QUEUE:  Memory grows + Frame shows past                            ║
║  ABSORBER: Memory flat + Frame shows now                            ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

AI-Human Concurrency

╔═════════════════════════════════════════════════════════════════════╗
║  WHY STATE ABSORBER MATTERS FOR AI                                  ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  Human types "ls" while AI generates output                         ║
║                                                                     ║
║  QUEUE (Problem):                                                   ║
║  ─────────────────────────────────────────────────────────────────  ║
║  T=1s: shows AI line 50  (queue: 950 pending)                       ║
║        Human input: in queue!                                       ║
║                                                                     ║
║  T=5s: shows AI line 250 (queue: 4750 pending)                      ║
║        Human input: still waiting!                                  ║
║                                                                     ║
║  ──▶ User sees PAST, input feedback delayed                         ║
║                                                                     ║
║  ─────────────────────────────────────────────────────────────────  ║
║                                                                     ║
║  ABSORBER (MonoTerm):                                               ║
║  ─────────────────────────────────────────────────────────────────  ║
║  T=1s: shows current (all coalesced)                                ║
║        Human input: visible!                                        ║
║                                                                     ║
║  T=5s: shows current (still synchronized)                           ║
║        Human input: still visible!                                  ║
║                                                                     ║
║  ──▶ User sees NOW, input feedback immediate                        ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝

Summary

╔═════════════════════════════════════════════════════════════════════╗
║  STATE ABSORBER: KEY TAKEAWAYS                                      ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                     ║
║  1. State Absorber OVERWRITES instead of QUEUES                     ║
║  2. All data is PROCESSED, only intermediate STATES discarded       ║
║  3. Memory is FIXED regardless of output volume                     ║
║  4. User always sees CURRENT state, not past                        ║
║  5. Pipeline NEVER blocks (decouples processing from rendering)     ║
║                                                                     ║
║  ─────────────────────────────────────────────────────────────────  ║
║                                                                     ║
║  State Absorber is the KEY component:                               ║
║                                                                     ║
║  • Without it: Actor + ACK would need queue ──▶ memory growth       ║
║  • With it: Actor processes fast, ACK controls, memory fixed        ║
║                                                                     ║
║  TWO GUARANTEES from ONE mechanism:                                 ║
║  • Memory Stability                                                 ║
║  • Temporal Consistency                                             ║
║                                                                     ║
╚═════════════════════════════════════════════════════════════════════╝