# Message Queue Composition in Munder Difflin: When Agents Can Type into Terminals

> Discover Munder Difflin's message queue composition and learn the five critical conditions agents must meet to type into terminals. Understand when deliveries can occur.

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

---

**Munder Difflin uses a single per-agent MD queue stored in Zustand to hold pending messages, and agents can only type into terminals when five conditions are met: idle status, no auto-pause (or manual release), completed boot grace, terminal automation safety, and minimum 4.5-second spacing between deliveries.**

Understanding the **message queue composition** in Munder Difflin is essential for anyone building automation harnesses around Claude Code. The repository implements a strict, policy-driven gating system that prevents AI agents from interrupting users or corrupting in-flight terminal sessions. This article breaks down the queue architecture, the safety gates, and the precise conditions that allow an agent to write to a PTY.

## Two Queues: What Munder Difflin Controls vs. Claude Code

Munder Difflin manages only one of two queues involved in message delivery. Understanding the boundary prevents architectural confusion when debugging delayed messages.

| Queue | Location | Purpose |
|------|----------|---------|
| **MD queue** | Harness Zustand store (per-agent) | Holds messages Munder Difflin has accepted and parked until the terminal is free |
| **Claude queue** | Inside Claude Code itself | Holds text Claude Code has accepted but not yet started on—**the harness never sees this** |

The rest of this article focuses exclusively on the **MD queue** that Munder Difflin owns and operates.

## MD Queue Composition and Storage

The **MD queue** lives in the harness's Zustand store at [`src/renderer/src/store/store.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/store/store.ts). It is a simple array per agent ID:

- **Enqueue point**: `enqueueMessage(agentId, text)`—called by composers, Slack ingress, scheduled jobs, and inbox nudges
- **Dequeue point**: The **drain loop** (effect #4 in [`src/renderer/src/hooks/useHive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/hooks/useHive.ts))—the **only code that ever writes to a live terminal**

This single-writer design eliminates race conditions between multiple automation sources.

## The Five Conditions for Terminal Access

The drain loop evaluates these conditions on every store change (debounced 200 ms) plus a 3-second back-stop tick. **All must be true** for delivery:

| Condition | Implementation | Rationale |
|-----------|---------------|-----------|
| Agent status is `idle` | `agent.status === 'idle'` in store | Prevents interrupting a running turn or active tool execution |
| Auto-delivery not paused **or** message manually released | `!autoPause \|\| msg.manual` | Supports floor-wide pausing via Command Center with per-message override |
| Past boot grace window | `pastBootGrace` flag | Allows CLI startup banners to complete before automation begins |
| `isTerminalAutomationSafe(ptyId)` returns true | [`terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/terminalPool.ts) | Verifies user still owns the prompt (see next section) |
| ≥ 4.5 s since last delivery | `timeSinceLast > 4500` | Prevents rapid writes that would jam TUI-based interfaces |

When a pause is active, queued rows display a **Send now** link that sets `manual: true`, bypassing only the pause check while preserving all other gates.

## The "User Owns the Prompt" Safety Gate

`isTerminalAutomationSafe` in [`src/renderer/src/components/terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/terminalPool.ts) blocks delivery when any protection flag is active:

| Flag | Set When | Cleared When |
|------|----------|--------------|
| `exited` | PTY process died | PTY respawns |
| `picker` | `/model`-style command opened a picker menu | **Enter**, **Escape**, **Ctrl-C** typed into terminal, or 30-minute expiry (`STALE_PICKER_MS`) |
| `draft` | Unsubmitted text detected on prompt line | Submitting, clearing, or 30-minute expiry (`STALE_INPUT_MS`) |
| `settling` | Short repaint window after line was freed | Time elapsed |

**Critical behavior**: Expiration of `picker` or `draft` does **not** erase user text or close open menus. The queued message types **after** whatever already exists on the line.

Reading the prompt line uses actual xterm buffer inspection (`term.buffer.active` in [`terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/terminalPool.ts)) rather than keystroke counting—ensuring the system never inadvertently erases drafts or closes pickers.

## The Drain Loop: High-Level Flow

```text
composer / Slack ingress ──► enqueueMessage(agentId, text) ──► MD queue ──► drain (effect #4) ──► PTY
scheduled /compact (effect #6) ──► …

```

Key implementation files:

- **[`src/renderer/src/hooks/useHive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/hooks/useHive.ts)** — inbox nudge (effect #3), drain loop (effect #4), scheduled `/compact` (effect #6)
- **[`src/renderer/src/components/terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/terminalPool.ts)** — `isTerminalAutomationSafe`, draft detection via buffer reading
- **[`src/renderer/src/components/terminalAutomation.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/terminalAutomation.ts)** — policy logic for blocking and expiry timing
- **[`src/renderer/src/store/store.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/store/store.ts)** — Zustand store containing per-agent MD queues
- **[`docs/message-queue.md`](https://github.com/chaitanyagiri/munder-difflin/blob/main/docs/message-queue.md)** — full contract specification

## Code Examples

Enqueueing a message from any producer:

```js
// From composer, Slack ingress, or scheduled job
import { enqueueMessage } from 'src/renderer/src/store/store';

enqueueMessage('agent-42', 'npm install lodash');

```

The drain loop's safety check (from [`useHive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/useHive.ts), effect #4):

```ts
if (
  agent.status === 'idle' &&
  !autoPause &&
  pastBootGrace &&
  isTerminalAutomationSafe(ptyId) &&
  timeSinceLast > 4500
) {
  const msg = mdQueue[agentId].shift();   // take front message
  pty.write(msg.text);                    // type the text
  pty.write('\r');                        // submit (Enter)
}

```

## Summary

- Munder Difflin maintains **one MD queue per agent** in its Zustand store—Claude Code's internal queue is invisible to the harness
- The **drain loop in [`useHive.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/useHive.ts)** is the sole writer to PTYs
- Terminal access requires **five simultaneous conditions**: idle status, no pause (or manual release), completed boot grace, `isTerminalAutomationSafe`, and 4.5-second spacing
- The **"user owns the prompt" gate** ([`terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/terminalPool.ts)) protects against automation during pickers, drafts, exits, and settle windows with 30-minute stale timeouts
- Buffer reading in [`terminalPool.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/terminalPool.ts) ensures **read-only inspection**—user state is never destroyed

## Frequently Asked Questions

### What happens if I click "Send now" during an auto-pause?

The message receives `manual: true` and moves to the front of the queue, bypassing **only** the pause check. All other conditions—idle status, boot grace, terminal safety, and timing—must still pass before the agent types into the terminal.

### Can Munder Difflin see what's in Claude Code's internal queue?

No. The **Claude queue** exists entirely inside Claude Code and is opaque to the harness. Latency you observe may originate in either queue; only the MD queue is observable and controllable via Munder Difflin's UI and APIs.

### Why does the system wait 4.5 seconds between deliveries?

The 4500 ms threshold prevents rapid back-to-back writes that would overwhelm TUI-based terminals (such as those running interactive CLI tools). This spacing ensures visual stability and prevents input jamming in complex terminal applications.

### What clears a `picker` block besides the 30-minute timeout?

A `picker` flag clears when the user types **Enter**, **Escape**, or **Ctrl-C** into the terminal. These keystrokes signal intentional dismissal of the picker menu, at which point automation can safely resume.