# What Is the Two‑Plane Architecture of Munder Difflin? A Deep‑Dive into Terminal and Event Planes

> Explore Munder Difflin's two-plane architecture. Understand how terminal I/O and event coordination are separated for efficient CLI processes and decoupled UI logic.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: deep-dive
- Published: 2026-08-28

---

**Munder Difflin uses a two‑plane architecture that splits terminal I/O handling from high‑level event coordination, enabling real CLI processes via node‑pty while keeping UI logic decoupled through a hook‑based event system.**

This design pattern powers Munder Difflin’s ability to run authentic terminal agents alongside rich, React‑driven visualizations. Rather than simulating shells in JavaScript, the system spawns genuine pseudo‑terminals (the **terminal plane**) and layers a structured communication bus (the **event plane**) on top for orchestration and UI updates.

---

## The Two Planes Explained

The architecture deliberately separates concerns into two distinct data planes that converge on a single renderer.

### Terminal Plane: Real PTY Processes

The **terminal plane** creates and manages actual operating‑level pseudo‑terminals for every agent. This guarantees byte‑perfect compatibility with any CLI tool, shell, or TUI application.

- **Core implementation**: [`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts) wraps **node‑pty** to spawn processes
- **Pool management**: [`src/renderer/src/components/terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/terminalPool.ts) maintains terminal instances
- **Output handling**: Raw byte streams travel over Electron IPC to **xterm.js** or **Pixi.js** renderers

Because the terminal plane deals only in raw PTY data, agents behave exactly as they would in a standalone terminal—colors, ANSI sequences, cursor positioning, and interactive prompts all work without emulation bugs.

### Event Plane: Structured Lifecycle Hooks

The **event plane** intercepts and broadcasts high‑level events without interfering with byte streams. It provides a publish/subscribe API that components use to react to agent state changes.

- **Core implementation**: [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) hosts the hook‑based event emitter (the "hive")
- **Launch coordination**: [`src/main/workerLaunch.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/workerLaunch.ts) orchestrates agent startup through the event plane
- **Consumption pattern**: Renderer components subscribe to events like `agent:start`, `agent:exit`, or custom prompt signals

This plane enables cross‑cutting concerns—budget enforcement, circuit breakers, human‑approval gates, logging—without polluting the raw PTY layer.

---

## How the Planes Converge on One Renderer

Both planes feed into a unified **React + Pixi.js** frontend. The renderer composes three responsibilities:

1. **Draw terminal output** – Subscribes to IPC messages from the terminal plane and writes bytes to xterm.js or canvas textures
2. **Render UI chrome** – Uses event plane hooks to display status badges, progress indicators, and control panels
3. **Coordinate multi‑agent views** – Manages layout and focus across many concurrent terminal sessions

The separation means UI engineers never touch PTY internals, while systems engineers can extend orchestration logic without breaking terminal rendering.

---

## Code Walkthrough: Each Plane in Practice

### Spawning a Terminal (Terminal Plane)

Located in [`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts), the wrapper forwards raw PTY data across the Electron main‑renderer boundary:

```typescript
// src/main/pty.ts
import * as pty from 'node-pty';

export function launchAgent(agentId: string, cmd: string, args: string[]) {
  const term = pty.spawn(cmd, args, {
    name: 'xterm-color',
    cols: 80,
    rows: 24,
    cwd: process.cwd(),
    env: process.env,
  });

  // Terminal plane: raw bytes → renderer
  term.onData(data => sendIpc('pty-data', { agentId, data }));
  term.onExit(info => sendIpc('pty-exit', { agentId, ...info }));
  return term;
}

```

### Broadcasting Events (Event Plane)

The hive in [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) offers a typed event bus for lifecycle coordination:

```typescript
// src/main/hive.ts
import { EventEmitter } from 'events';
export const hive = new EventEmitter();

export function announceStart(agentId: string) {
  hive.emit('agent:start', { agentId, timestamp: Date.now() });
}

// Subscription elsewhere
hive.on('agent:start', ({ agentId }) => {
  console.log(`🟢 Agent ${agentId} is now running`);
});

```

### Consuming Both Planes in the Renderer

[`src/renderer/src/components/TerminalView.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/TerminalView.tsx) demonstrates dual consumption—raw terminal data for display, events for state management:

```tsx
// src/renderer/src/components/TerminalView.tsx
import { useEffect } from 'react';
import { ipcRenderer } from 'electron';

export function TerminalView({ agentId }: { agentId: string }) {
  useEffect(() => {
    const handleData = (_: any, { agentId: id, data }: any) => {
      if (id === agentId) termRef.current?.write(data);
    };
    ipcRenderer.on('pty-data', handleData);
    return () => ipcRenderer.removeListener('pty-data', handleData);
  }, [agentId]);

  // Pixi.js canvas receives terminal texture
  return <PixiTerminalCanvas agentId={agentId} />;
}

```

---

## Why Two Planes?

| Benefit | How the two‑plane design delivers |
|--------|-----------------------------------|
| **Reliability** | node‑pty provides real terminals, eliminating string‑simulation edge cases |
| **Scalability** | Lightweight hooks in the event plane coordinate hundreds of agents without PTY overhead |
| **Extensibility** | New UI features subscribe to events; new orchestration logic ignores rendering |
| **Testability** | Event plane can be mocked; terminal plane can be replaced with fixtures |

---

## Summary

- **Terminal plane** ([`src/main/pty.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/pty.ts), [`terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/terminalPool.ts)) handles raw PTY I/O via node‑pty
- **Event plane** ([`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts), [`workerLaunch.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/workerLaunch.ts)) provides hook‑based lifecycle coordination
- **Single renderer** combines React and Pixi.js to present both raw terminal output and structured UI state
- The split enables authentic CLI compatibility alongside rich, event‑driven orchestration

---

## Frequently Asked Questions

### What problem does the two‑plane architecture solve?

The two‑plane architecture solves the tension between **authentic terminal behavior** and **rich application orchestration**. Simulating terminals in JavaScript introduces bugs with complex TUIs, while exposing raw PTY internals to UI code creates fragile coupling. The split lets Munder Difflin run real shells and still coordinate them through clean, testable hooks.

### Can I use the event plane without the terminal plane?

Yes. The event plane in [`src/main/hive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) is an independent EventEmitter. You can emit and subscribe to events for mock agents, background jobs, or external integrations without spawning any PTY processes. This is useful for testing orchestration logic or integrating non‑terminal workloads.

### How does node‑pty differ from child_process.spawn?

**node‑pty** creates a pseudo‑terminal (PTY), allocating a TTY device that programs detect as an interactive terminal. This enables color output, readline behavior, and TUI rendering. **child_process.spawn** creates a standard pipe, which many CLI tools run in non‑interactive mode, suppressing colors and prompts. Munder Difflin’s terminal plane requires PTYs for faithful agent reproduction.

### Where is the authoritative design documentation?

The original architecture rationale lives in [`blog/src/posts/architecture-two-planes-one-renderer.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/blog/src/posts/architecture-two-planes-one-renderer.md) ([source](https://github.com/chaitanyagiri/munder-difflin/blob/main/blog/src/posts/architecture-two-planes-one-renderer.md)). This file preceded the implementation and remains the reference for design decisions around plane separation and renderer unification.